This is an R Markdown Notebook for analysis using data on the DC Bus System (WMATA Metrobus). The data were obtained here:
https://planitmetro.com/2016/11/16/data-download-metrobus-vehicle-location-data/
Load the packages to be used.
Get the Bus data.
First let’s check the working directory.
getwd()
Then, actually get the data.
Put the daily data together.
AllDays <- bind_rows(list(Oct03Raw, Oct04Raw, Oct05Raw, Oct06Raw, Oct07Raw),
.id = c("group")
)
# dim(AllDays)
str(AllDays)
Deleting old data frames.
for (i in 3:7){
rm(list = ls(pattern = paste0("Oct0", i, "Raw")
)
)
message("Deleting Oct0", i, "Raw")
}
Updating variable types.
Then, sorting the data and adding a RowNumber (to be used for identifying rows later in the analyses.)
rm(i)
AllDays$group <- factor(AllDays$group)
AllDays$Route_Direction <- factor(AllDays$Route_Direction)
AllDays$Event_Time <- as.POSIXct(AllDays$Event_Time, format = "%m-%d-%y %I:%M:%S %p")
AllDays$Departure_Time <- as.POSIXct(AllDays$Departure_Time, format = "%m-%d-%y %I:%M:%S %p")
str(AllDays)
AllDays_Sorted <- arrange(AllDays,
Bus_ID,
Event_Time
) %>%
mutate(RowNum_OG = row_number() # this is useful in identify the row later on
)
rm(AllDays)
str(AllDays_Sorted)
# View(head(AllDays_Sorted, 100))
Inspecting the values of Stop_ID, and finding that it can take the values “” (blank) and “NULL”.
View(group_by(AllDays_Sorted,
Stop_ID
) %>%
summarise(
Cnt = n()
) %>%
arrange(Stop_ID)
)
View(filter(AllDays_Sorted,
is.na(Stop_ID) |
Stop_ID == "" |
Stop_ID == "NULL"
) %>%
arrange(Stop_Desc)
)
Creating a table of distinct Stop_Desc values when Stop_ID is “” (blank) or “NULL”.
StopID_New <- filter(AllDays_Sorted,
is.na(Stop_ID) |
Stop_ID == "" |
Stop_ID == "NULL"
) %>%
select(Stop_ID, Stop_Desc) %>%
distinct() %>%
arrange(Stop_ID, Stop_Desc) %>%
mutate(StopID_New = 1:nrow(.)
)
View(StopID_New)
StopID_New
Creating a full updated table by filling in StopID_New for when Stop_ID is “” (blank) or NULL.
AllDays_StopIDNew <- left_join(AllDays_Sorted,
select(StopID_New,
Stop_Desc,
StopID_New
),
by = c("Stop_Desc" = "Stop_Desc")
) %>%
mutate(StopID_Clean = ifelse(is.na(StopID_New),
Stop_ID,
StopID_New
),
StopID_Indicator = factor(ifelse(is.na(StopID_New),
"ID_OK",
"ID_Bad"
)
)
)
rm(StopID_New)
rm(AllDays_Sorted)
str(AllDays_StopIDNew)
# View(tail(AllDays_StopIDNew, 500))
# View(filter(AllDays_StopIDNew,
# Stop_Desc == "METROWAY ANNNOUCEMNT CORR"
# )
# )
Lat Long stats for pulling in Zip codes later.
LL_Stats <- group_by(AllDays_StopIDNew,
StopID_Clean
) %>%
summarise(Lat_Mean = mean(Latitude, na.rm = TRUE),
Lat_Med = median(Latitude, na.rm = TRUE),
Lng_Mean = mean(Longitude, na.rm = TRUE),
Lng_Med = median(Longitude, na.rm = TRUE)
) %>%
mutate(Lat_MeaLessMed = Lat_Mean - Lat_Med,
Lng_MeaLessMed = Lng_Mean - Lng_Med,
RowNum = row_number()
)
str(LL_Stats)
summary(LL_Stats)
View(head(arrange(LL_Stats,
Lat_MeaLessMed
),
500
)
)
View(head(arrange(LL_Stats,
desc(Lat_MeaLessMed)
),
500
)
)
View(head(arrange(LL_Stats,
Lng_MeaLessMed
),
500
)
)
View(head(arrange(LL_Stats,
desc(Lng_MeaLessMed)
),
500
)
)
Pulling in Zip Code data from api.geonames.org.
# URL EXAMPLE:
# http://api.geonames.org/findNearbyPostalCodesJSON?lat=38.89560&lng=-76.94873&radius=0&username=supermdat
url_1 <- "http://api.geonames.org/findNearbyPostalCodesJSON?lat="
url_2 <- "&lng="
url_3 <- "&radius=0&username="
username <- "supermdat"
# need to group in bunches as http://api.geonames.org limits pulls to 2000 per hour
##### Store everything in multiple lists
pages1 <- list()
system.time(
for(i in 1:1000){
lat <- filter(LL_Stats,
RowNum == i
) %>%
select(Lat_Med)
lng <- filter(LL_Stats,
RowNum == i
) %>%
select(Lng_Med)
APIData1 <- fromJSON(paste0(url_1,
lat,
url_2,
lng,
url_3,
username
),
flatten = TRUE
)
message("Retrieving Zip Code ", i)
pages1[[i]] <- APIData1$postalCodes
}
)
##### Combine the lists into one page
Zips1 <- rbind.pages(pages1[sapply(pages1, length) > 0])
##### Combine all pages
Zips_All <- bind_rows(Zips0,
Zips1,
Zips2,
Zips3,
Zips4,
Zips5,
Zips6,
Zips7,
Zips8,
Zips9,
Zips10,
.id = "id"
) %>%
mutate(UniqueLatLng = paste(lat, lng, sep = "__")
)
# str(Zips_All)
# View(head(Zips_All))
# str(LL_Stats)
LL_Stats_UnqLatLng <- mutate(LL_Stats,
UniqueLatLng = paste(Lat_Med, Lng_Med, sep = "__")
)
# str(LL_Stats_UnqLatLng)
# View(head(LL_Stats_UnqLatLng))
LL_StatsZips <- left_join(LL_Stats_UnqLatLng,
Zips_All,
by = c("UniqueLatLng" = "UniqueLatLng")
)
str(LL_StatsZips)
# View(head(LL_StatsZips))
# Not sure whey these couldn't be found (why they're NA)
View(filter(LL_StatsZips,
is.na(postalCode)
)
)
Join to create one dataset that also includes Zip variables.
rm(url_1, url_2, url_3, username, pages0, pages1, pages2, pages3, pages4, pages5, pages6, pages7, pages8, pages9, pages10, i, lat, lng, APIData0, APIData1, APIData2, APIData3, APIData4, APIData5, APIData6, APIData7, APIData8, APIData9, APIData10, LL_Stats, LL_Stats_UnqLatLng)
AllDays_Zips <- left_join(AllDays_StopIDNew,
LL_StatsZips,
by = c("StopID_Clean" = "StopID_Clean")
) %>%
rename(Stop_State = adminCode1,
Stop_County = adminName2,
Stop_City = placeName,
Stop_Zip = postalCode
)
rm(AllDays_StopIDNew, LL_StatsZips)
str(AllDays_Zips)
Updating variable types.
AllDays_Zips$Stop_State <- factor(AllDays_Zips$Stop_State)
AllDays_Zips$Stop_County <- factor(AllDays_Zips$Stop_County)
AllDays_Zips$Stop_Zip <- factor(AllDays_Zips$Stop_Zip)
AllDays_Zips$Stop_City <- factor(AllDays_Zips$Stop_City)
AllDays_Zips$distance <- as.numeric(AllDays_Zips$distance)
AllDays_Zips$countryCode <- factor(AllDays_Zips$countryCode)
AllDays_Zips$adminName1 <- factor(AllDays_Zips$adminName1)
str(AllDays_Zips)
Feature engineering.
Inspecting incidences of consecutive Stop_IDs. This is done because investigation showed that many conseutive events occurr at the same Stop_ID, but with various Dwell_Times, Odometer_Distances, etc. All of which affect calculations and analyses.
Create data on the runs (consecutive Stop_IDs).
StopID_Runs <- rle(AllDays_Zips$StopID_Clean)
StopID_Runs$ends <- cumsum(StopID_Runs$lengths)
StopID_Runs$starts <- ifelse(is.na(lag(StopID_Runs$ends)
),
1,
lag(StopID_Runs$ends) + 1
)
str(StopID_Runs)
# class(StopID_Runs)
#
# StopID_Runs_df <- data.frame(unclass(StopID_Runs))
# str(StopID_Runs_df)
# class(StopID_Runs_df)
# rm(StopID_Runs_df)
Trying to link data on RunsGroups with the original data (AllDays_Sorted). The goal is to select only one record per RunsGroup - that being the record with the longest Dwell_Time.
I attempted this computation using both data.frames (dplyr) and data.tables (data.table). However, with 2,809,062 rows in one dataset and 3,119,443 rows in the other dataset, the current computation time is over 5 days…so I’m trying a different strategy to only select the first record in a run.
# Create a RunsGroup variable for each run
# StopID_Runs_df$RunsGroup <- paste0("g", seq(1:nrow(StopID_Runs_df)
# )
# )
#
# str(StopID_Runs_df)
# head(StopID_Runs_df, 25)
# tail(StopID_Runs_df, 25)
#
# StopID_Runs_df <- StopID_Runs_df %>%
# mutate(RowNum = row_number()
# )
#
# str(StopID_Runs_df)
# head(StopID_Runs_df, 25)
# tail(StopID_Runs_df, 25)
#
#
# # Converting to data.tables for, hopefully, improved performance (speed) in computation
# StopID_Runs_dt <- data.table(StopID_Runs_df)
# setkey(StopID_Runs_dt, RowNum)
# str(StopID_Runs_dt)
#
# AllDays_Sorted_dt <- data.table(AllDays_Sorted)
# setkey(AllDays_Sorted_dt, RowNum_OG)
# str(AllDays_Sorted_dt)
# # rm(AllDays_Sorted_dt)
#
#
# # Actual loop to perform the computations and link to original data (AllDays_Sorted_dt)
# GroupData <- list()
# for(i in 1:nrow(StopID_Runs_dt)
# ) {
# assign(paste0("group_", i),
# StopID_Runs_dt[RowNum == i, RunsGroup]
# )
#
# ##### The code below is the same code as above, but done with dplyr #####
#
# # assign(paste0("group_", i),
# # filter(StopID_Runs_df,
# # RowNum == i
# # ) %>%
# # select(RunsGroup)
# # )
#
# assign(paste0("group_", i, "_start"),
# StopID_Runs_dt[RowNum == i, starts]
# )
#
# assign(paste0("group_", i, "_end"),
# StopID_Runs_dt[RowNum == i, ends]
# )
#
# assign(paste0("group_", i, "_rows"),
# AllDays_Sorted_dt[RowNum_OG >= as.numeric(get(paste0("group_", i, "_start")
# )
# ) &
# RowNum_OG <= as.numeric(get(paste0("group_", i, "_end")
# )
# ),
# RunsGroup := as.character(get(paste0("group_", i)
# )
# )
# ]
#
# ##### The code below is the same as the code above, but done with dplyr #####
#
# # filter(AllDays_Sorted,
# # between(RowNum_OG,
# # as.numeric(get(paste0("group_", i, "_start")
# # )
# # ),
# # as.numeric(get(paste0("group_", i, "_end")
# # )
# # )
# # )
# # ) %>%
# # mutate(RunsGroup = as.character(get(paste0("group_", i)
# # )
# # )
# # )
# )
#
# GroupData[[i]] <- get(paste0("group_", i, "_rows"))
#
# message("Processing Group ", i, " of 2,809,062")
# }
#
#
# GroupData_df <- rbind.fill(GroupData)
# str(GroupData_df)
# head(GroupData_df)
# tail(GroupData_df)
# # rm(GroupData_df)
#
#
# group_1
# group_1_start
# group_1_end
# group_1_rows
# group_2_rows
# group_3_rows
# group_50_rows
# str(group_50_rows)
# group_2809062_rows
# GroupData[[1]]
# GroupData[[50]]
#
#
# ##### Testing Area (Below) #####
# ##### Testing Area (Below) #####
# ##### Testing Area (Below) #####
#
# # head(StopID_Runs$starts, 20)
# # head(AllDays_NewOrder$Stop_ID, 20)
# #
# #
# # dat <- as.data.frame(c(1,1,7,7,7,9,6,8,2,2,2,1,1,1,1,1))
# # colnames(dat)[1] <- "dat"
# # r <- rle(dat$dat)
# # dat$run <- rep(r$lengths, r$lengths)
# # dat$runLag <- lag(dat$run)
# # dat$cond <- rep(r$values, r$lengths)
# # dat
# # View(dat)
When consecutive Stop_ID occurrs, only take the first occurrence. This is done because the computation time to select only the record with the longest Dwell_Time for each run was too long (over 5 days).
This is probably less than ideal with regards to Dwell_Time, but should not make much difference for calculations of travel time, speed, etc.
AllDays_FirstStopID <- AllDays_Zips[StopID_Runs$starts, ]
dim(AllDays_Zips)
dim(AllDays_FirstStopID)
nrow(AllDays_Zips) - nrow(AllDays_FirstStopID)
rm(AllDays_Zips, StopID_Runs)
str(AllDays_FirstStopID)
Feature engineering.
Creating new variables.
AllDays_AddVars <- mutate(AllDays_FirstStopID,
Odometer_Distance_Mi = Odometer_Distance / 5280, #5,280 feet in 1 mile
Dwell_Time2 = as.numeric(Departure_Time - Event_Time),
Event_Time_Yr = as.integer(year(Event_Time)),
Event_Time_Mth = as.integer(month(Event_Time)),
Event_Time_Date = day(Event_Time),
Event_Time_Day = wday(Event_Time, label = TRUE),
Event_Time_Hr = hour(Event_Time),
Event_Time_Min = minute(Event_Time),
Event_Time_HrGroup = factor(ifelse(Event_Time_Hr < 3,
"Group0_2",
ifelse(Event_Time_Hr < 6,
"Group3_5",
ifelse(Event_Time_Hr < 9,
"Group6_8",
ifelse(Event_Time_Hr < 12,
"Group9_11",
ifelse(Event_Time_Hr < 15,
"Group12_14",
ifelse(Event_Time_Hr < 18,
"Group15_17",
ifelse(Event_Time_Hr < 21,
"Group18_20",
ifelse(Event_Time_Hr < 24,
"Group21_23"
)))))))),
levels = c("Group0_2",
"Group3_5",
"Group6_8",
"Group9_11",
"Group12_14",
"Group15_17",
"Group18_20",
"Group21_23"
),
ordered = TRUE
)
)
rm(AllDays_FirstStopID)
str(AllDays_AddVars)
Function for calculating the distance traveled based on the Haversine formula. Original code from: https://www.r-bloggers.com/great-circle-distance-calculations-in-r/
# Calculates the geodesic distance between two points specified by radian latitude/longitude using the Haversine formula (hf)
# gcd.hf <- function(long1, lat1, long2, lat2) {
# R <- 6371 # Earth mean radius [km]
# delta.long <- (long2 - long1)
# delta.lat <- (lat2 - lat1)
# a <- sin(delta.lat/2)^2 + cos(lat1) * cos(lat2) * sin(delta.long/2)^2
# c <- 2 * asin(min(1,sqrt(a)))
# d = R * c * 0.621371 # 1 km = 0.621371 miles
# return(d) # Distance in miles
# }
Feature engineering.
Creating more variables. Creating a BusEvent row number for future identification purposes. Then, creating various variables to analyze distance traveled and speed.
AllDays_BusDay <- group_by(AllDays_AddVars,
Bus_ID,
Event_Time_Date
) %>%
mutate(BusDay_EventNum = row_number(), # used to identify Bus movements on a particular date
Route_Lag1 = lag(Route), # used in future analyses to identify Route changes
RouteAlt_Lag1 = lag(RouteAlt), # used in future analyses to identify RouteAlt (direction) changes
Odometer_Distance_Lag1 = lag(Odometer_Distance),
Latitude_L1 = lag(Latitude),
Longitude_L1 = lag(Longitude),
# Lat_Radian = Latitude*pi/180,
# Long_Radian = Longitude*pi/180,
# Lat_Radian_L1 = lag(Lat_Radian),
# Long_Radian_L1 = lag(Long_Radian),
# accounting for potential negative distances
TravelDistance_Ft = ifelse(Odometer_Distance > Odometer_Distance_Lag1,
Odometer_Distance - Odometer_Distance_Lag1,
NA
),
TravelDistance_Mi = TravelDistance_Ft / 5280, #5,280 feet in 1 mile
# TravelDistance_Mi2 = gcd.hf(long1 = Long_Radian_L1,
# lat1 = Lat_Radian_L1,
# long2 = Long_Radian,
# lat2 = Lat_Radian
# ),
TravelDistance_Mi_Hvrs =
# ifelse((is.na(Longitude_L1) | is.na(Latitude_L1)
# ),
# NA,
distHaversine(cbind(Longitude_L1, Latitude_L1),
cbind(Longitude, Latitude)
) * 0.000621371, # 0.000621371 miles = 1 meter
# accounting for potential negative times
TravelTime_Sec = as.numeric(ifelse(Event_Time > lag(Departure_Time),
Event_Time - lag(Departure_Time),
NA
)
),
TravelTime_Hr = TravelTime_Sec / 3600, # 3,600 seconds in 1 hour
# accounting for potential negative or zero travel times
SpeedAvg_Mph = ifelse(TravelTime_Hr > 0,
TravelDistance_Mi / TravelTime_Hr,
NA
),
Start_ID = lag(StopID_Clean),
Start_Desc = lag(Stop_Desc),
StartStop_ID = ifelse(is.na(Start_ID),
paste("NULL", StopID_Clean, sep = "--"),
paste(Start_ID, StopID_Clean, sep = "--")
)
) %>%
as.data.frame()
rm(AllDays_AddVars)
str(AllDays_BusDay)
# summary(AllDays_BusDay)
# View(tail(AllDays_BusDay, 50))
Inspecting for issues with StartStop_ID (where the value is either NA or contains NULL). They ONLY exist when BusDay_EventNum = 1 (which is by design). So everything looks OK.
View(group_by(AllDays_BusDay,
StartStop_ID
) %>%
summarise(
Cnt = n()
) %>%
arrange(desc(Cnt)
)
)
View(filter(AllDays_BusDay,
(is.na(StartStop_ID) |
str_detect(StartStop_ID, "NULL")
) &
BusDay_EventNum != 1
)
)
Stats (quantiles) overall for TravelDistance_Mi.
Quantiles_dt <- AllDays_BusDay %>%
mutate(TD_Mi_q2 = quantile(x = TravelDistance_Mi, probs = 0.02, na.rm = TRUE),
TD_Mi_q98 = quantile(x = TravelDistance_Mi, probs = 0.98, na.rm = TRUE),
TT_Sec_q2 = quantile(x = TravelTime_Sec, probs = 0.02, na.rm = TRUE),
TT_Sec_q98 = quantile(x = TravelTime_Sec, probs = 0.98, na.rm = TRUE),
TT_Hr_q2 = quantile(x = TravelTime_Hr, probs = 0.02, na.rm = TRUE),
TT_Hr_q98 = quantile(x = TravelTime_Hr, probs = 0.98, na.rm = TRUE)
) %>%
data.table()
Stats <- Quantiles_dt %>%
mutate(TD_Mi_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
TD_Mi_Mean_F = mean(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98],
na.rm = TRUE
),
TD_Mi_Med = median(TravelDistance_Mi, na.rm = TRUE),
TD_Mi_Med_F = median(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98],
na.rm = TRUE
),
TD_Mi_Cnt = sum(!is.na(TravelDistance_Mi)
),
TD_Mi_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98]
)
),
TT_Sec_Mean = mean(TravelTime_Sec, na.rm = TRUE),
TT_Sec_Mean_F = mean(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98],
na.rm = TRUE
),
TT_Sec_Med = median(TravelTime_Sec, na.rm = TRUE),
TT_Sec_Med_F = median(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98],
na.rm = TRUE
),
TT_Sec_Cnt = sum(!is.na(TravelTime_Sec)
),
TT_Sec_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98]
)
),
TT_Hr_Mean = mean(TravelTime_Hr, na.rm = TRUE),
TT_Hr_Mean_F = mean(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98],
na.rm = TRUE
),
TT_Hr_Med = median(TravelTime_Hr, na.rm = TRUE),
TT_Hr_Med_F = median(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98],
na.rm = TRUE
),
TT_Hr_Cnt = sum(!is.na(TravelTime_Hr)
),
TT_Hr_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98]
)
)
) %>%
data.frame()
rm(AllDays_BusDay)
rm(Quantiles_dt)
str(Stats)
# View(head(Stats, 50))
Stats for StartStop_ID.
Quantiles_SS_dt <- group_by(Stats,
StartStop_ID
) %>%
mutate(TD_Mi_SS_q5 = quantile(x = TravelDistance_Mi, probs = 0.05, na.rm = TRUE),
TD_Mi_SS_q95 = quantile(x = TravelDistance_Mi, probs = 0.95, na.rm = TRUE),
TT_Sec_SS_q5 = quantile(x = TravelTime_Sec, probs = 0.05, na.rm = TRUE),
TT_Sec_SS_q95 = quantile(x = TravelTime_Sec, probs = 0.95, na.rm = TRUE),
TT_Hr_SS_q5 = quantile(x = TravelTime_Hr, probs = 0.05, na.rm = TRUE),
TT_Hr_SS_q95 = quantile(x = TravelTime_Hr, probs = 0.95, na.rm = TRUE)
) %>%
data.table()
Stats_StSt <- group_by(Quantiles_SS_dt,
StartStop_ID
) %>%
mutate(TD_Mi_SS_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
TD_Mi_SS_Mean_F = mean(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95],
na.rm = TRUE
),
TD_Mi_SS_Med = median(TravelDistance_Mi, na.rm = TRUE),
TD_Mi_SS_Med_F = median(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95],
na.rm = TRUE
),
TD_Mi_SS_Cnt = sum(!is.na(TravelDistance_Mi)
),
TD_Mi_SS_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95]
)
),
TT_Sec_SS_Mean = mean(TravelTime_Sec, na.rm = TRUE),
TT_Sec_SS_Mean_F = mean(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95],
na.rm = TRUE
),
TT_Sec_SS_Med = median(TravelTime_Sec, na.rm = TRUE),
TT_Sec_SS_Med_F = median(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95],
na.rm = TRUE
),
TT_Sec_SS_Cnt = sum(!is.na(TravelTime_Sec)),
TT_Sec_SS_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95]
)
),
TT_Hr_SS_Mean = mean(TravelTime_Hr, na.rm = TRUE),
TT_Hr_SS_Mean_F = mean(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95],
na.rm = TRUE
),
TT_Hr_SS_Med = median(TravelTime_Hr, na.rm = TRUE),
TT_Hr_SS_Med_F = median(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95],
na.rm = TRUE
),
TT_Hr_SS_Cnt = sum(!is.na(TravelTime_Hr)),
TT_Hr_SS_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95]
)
)
) %>%
data.frame()
rm(Stats)
rm(Quantiles_SS_dt)
str(Stats_StSt)
# View(head(Stats_StSt, 50))
Stats for StartStop_ID with Event_Time_HrGroup.
Quantiles_SSHG_dt <- group_by(Stats_StSt,
StartStop_ID,
Event_Time_HrGroup
) %>%
mutate(TD_Mi_SSHG_q5 = quantile(x = TravelDistance_Mi, probs = 0.05, na.rm = TRUE),
TD_Mi_SSHG_q95 = quantile(x = TravelDistance_Mi, probs = 0.95, na.rm = TRUE),
TT_Sec_SSHG_q5 = quantile(x = TravelTime_Sec, probs = 0.05, na.rm = TRUE),
TT_Sec_SSHG_q95 = quantile(x = TravelTime_Sec, probs = 0.95, na.rm = TRUE),
TT_Hr_SSHG_q5 = quantile(x = TravelTime_Hr, probs = 0.05, na.rm = TRUE),
TT_Hr_SSHG_q95 = quantile(x = TravelTime_Hr, probs = 0.95, na.rm = TRUE)
) %>%
data.table()
Stats_StSt_HrGrp <- group_by(Quantiles_SSHG_dt,
StartStop_ID,
Event_Time_HrGroup
) %>%
mutate(TD_Mi_SSHG_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
TD_Mi_SSHG_Mean_F = mean(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95],
na.rm = TRUE
),
TD_Mi_SSHG_Med = median(TravelDistance_Mi, na.rm = TRUE),
TD_Mi_SSHG_Med_F = median(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95],
na.rm = TRUE
),
TD_Mi_SSHG_Cnt = sum(!is.na(TravelDistance_Mi)
),
TD_Mi_SSHG_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95]
)
),
TT_Sec_SSHG_Mean = mean(TravelTime_Sec, na.rm = TRUE),
TT_Sec_SSHG_Mean_F = mean(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95],
na.rm = TRUE
),
TT_Sec_SSHG_Med = median(TravelTime_Sec, na.rm = TRUE),
TT_Sec_SSHG_Med_F = median(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95],
na.rm = TRUE
),
TT_Sec_SSHG_Cnt = sum(!is.na(TravelTime_Sec)),
TT_Sec_SSHG_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95]
)
),
TT_Hr_SSHG_Mean = mean(TravelTime_Hr, na.rm = TRUE),
TT_Hr_SSHG_Mean_F = mean(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95],
na.rm = TRUE
),
TT_Hr_SSHG_Med = median(TravelTime_Hr, na.rm = TRUE),
TT_Hr_SSHG_Med_F = median(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95],
na.rm = TRUE
),
TT_Hr_SSHG_Cnt = sum(!is.na(TravelTime_Hr)),
TT_Hr_SSHG_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95]
)
)
) %>%
data.frame()
rm(Stats_StSt)
rm(Quantiles_SSHG_dt)
str(Stats_StSt_HrGrp)
# View(head(Stats_StSt_HrGrp, 50))
Feature engineering.
Creating a BusEventRoute row number, and a RouteAlt_Lag1 indicator for future identification purposes.
# rm(Quantiles_dt)
# rm(Quantiles_SS_dt)
# rm(AllDays_BusDay)
# rm(Quantiles_SSHG_dt)
# rm(Stats_StSt)
# AllDays_BusDayRoute <- group_by(Stats_StSt_HrGrp,
# Bus_ID,
# Event_Time_Date,
# Route
# ) %>%
# mutate(RouteAlt_Lag2 = lag(RouteAlt) # used in future analyses to identify RouteAlt (direction) changes
#
# # Odometer_Distance_Lag1 = lag(Odometer_Distance),
# #
# # # accounting for potential negative distances
# # TravelDistance_Ft = ifelse(Odometer_Distance >= Odometer_Distance_Lag1,
# # Odometer_Distance - Odometer_Distance_Lag1,
# # NA
# # ),
# # TravelDistance_Mi = TravelDistance_Ft / 5280, #5,280 feet in 1 mile
# #
# # # accounting for potential negative times
# # TravelTime_Sec = as.numeric(ifelse(Event_Time >= lag(Departure_Time),
# # Event_Time - lag(Departure_Time),
# # NA
# # )
# # ),
# # TravelTime_Hr = TravelTime_Sec / 3600, # 3,600 seconds in 1 hour
# #
# # # accounting for potential negative or zero travel times
# # SpeedAvg_Mph = ifelse(TravelTime_Hr > 0,
# # TravelDistance_Mi / TravelTime_Hr,
# # NA
# # )
# ) %>%
# data.frame()
#
# rm(Stats_StSt_HrGrp)
# str(AllDays_BusDayRoute)
Feature engineering.
Calculating a variable to know if the RouteAlt changed. Could be useful in helping identifying weirdness in calculated distances and speeds.
# rm(Stats_StSt_HrGrp)
AllDays_DirChange <- Stats_StSt_HrGrp %>% # AllDays_BusDayRoute %>%
mutate(RteChange = ifelse(Route == Route_Lag1,
"Same",
"Change"
),
RteChange2 = factor(ifelse(is.na(RteChange),
"Change",
RteChange
)
),
DirChange = ifelse(RouteAlt == RouteAlt_Lag1,
"Same",
"Change"
),
DirChange2 = factor(ifelse(is.na(DirChange),
"Change",
DirChange
)
)
)
# rm(AllDays_BusDayRoute)
rm(Stats_StSt_HrGrp)
str(AllDays_DirChange)
View(filter(AllDays_DirChange,
between(RowNum_OG, 2570060, 2570080)
) %>%
select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
)
)
Re-ordering the variables to ease with comprehension.
AllDays_NewOrder <- select(AllDays_DirChange,
RowNum_OG,
UniqueLatLng,
group,
StartStop_ID,
BusDay_EventNum,
Bus_ID,
Route,
RteChange2,
RouteAlt,
# RouteAlt_Lag1,
DirChange2,
Route_Direction,
Stop_Sequence,
Start_ID,
Start_Desc,
# Stop_ID,
StopID_Clean,
StopID_Indicator,
Stop_Desc,
countryCode,
Stop_State,
Stop_County,
Stop_City,
Stop_Zip,
Event_Type,
Event_Description,
Event_Time_Yr,
Event_Time_Mth,
Event_Time_Date,
Event_Time_Day,
Event_Time_Hr,
Event_Time_HrGroup,
Event_Time_Min,
Event_Time,
Departure_Time,
Dwell_Time,
Dwell_Time2,
Delta_Time,
Latitude,
Longitude,
Heading,
Odometer_Distance,
Odometer_Distance_Lag1,
Odometer_Distance_Mi,
TravelDistance_Ft,
TravelDistance_Mi,
TravelDistance_Mi_Hvrs,
TD_Mi_q2,
TD_Mi_q98,
TD_Mi_SS_q5,
TD_Mi_SS_q95,
TD_Mi_SSHG_q5,
TD_Mi_SSHG_q95,
TD_Mi_Mean,
TD_Mi_Mean_F,
TD_Mi_SS_Mean,
TD_Mi_SS_Mean_F,
TD_Mi_SSHG_Mean,
TD_Mi_SSHG_Mean_F,
TD_Mi_Med,
TD_Mi_Med_F,
TD_Mi_SS_Med,
TD_Mi_SS_Med_F,
TD_Mi_SSHG_Med,
TD_Mi_SSHG_Med_F,
TD_Mi_Cnt,
TD_Mi_Cnt_F,
TD_Mi_SS_Cnt,
TD_Mi_SS_Cnt_F,
TD_Mi_SSHG_Cnt,
TD_Mi_SSHG_Cnt_F,
TravelTime_Sec,
TT_Sec_q2,
TT_Sec_q98,
TT_Sec_SS_q5,
TT_Sec_SS_q95,
TT_Sec_SSHG_q5,
TT_Sec_SSHG_q95,
TT_Sec_Mean,
TT_Sec_Mean_F,
TT_Sec_SS_Mean,
TT_Sec_SS_Mean_F,
TT_Sec_SSHG_Mean,
TT_Sec_SSHG_Mean_F,
TT_Sec_Med,
TT_Sec_Med_F,
TT_Sec_SS_Med,
TT_Sec_SS_Med_F,
TT_Sec_SSHG_Med,
TT_Sec_SSHG_Med_F,
TT_Sec_Cnt,
TT_Sec_Cnt_F,
TT_Sec_SS_Cnt,
TT_Sec_SS_Cnt_F,
TT_Sec_SSHG_Cnt,
TT_Sec_SSHG_Cnt_F,
TravelTime_Hr,
TT_Hr_q2,
TT_Hr_q98,
TT_Hr_SS_q5,
TT_Hr_SS_q95,
TT_Hr_SSHG_q5,
TT_Hr_SSHG_q95,
TT_Hr_Mean,
TT_Hr_Mean_F,
TT_Hr_SS_Mean,
TT_Hr_SS_Mean_F,
TT_Hr_SSHG_Mean,
TT_Hr_SSHG_Mean_F,
TT_Hr_Med,
TT_Hr_Med_F,
TT_Hr_SS_Med,
TT_Hr_SS_Med_F,
TT_Hr_SSHG_Med,
TT_Hr_SSHG_Med_F,
TT_Hr_Cnt,
TT_Hr_Cnt_F,
TT_Hr_SS_Cnt,
TT_Hr_SS_Cnt_F,
TT_Hr_SSHG_Cnt,
TT_Hr_SSHG_Cnt_F,
SpeedAvg_Mph
)
rm(AllDays_DirChange)
str(select(AllDays_NewOrder,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
)
)
str(AllDays_NewOrder)
# View(head(AllDays_NewOrder, 500))
# View(tail(AllDays_NewOrder, 500))
Summarizing the data to help spot anomolies.
View(group_by(AllDays_NewOrder,
Stop_City) %>%
summarise(Cnt_Num = n(),
Cnt_Pct = 100*Cnt_Num / (nrow(AllDays_NewOrder)
)
) %>%
arrange(desc(Cnt_Num))
)
summary(AllDays_NewOrder)
Investigation of TravelDistance_Mi.
View(TravDistMi_Pctiles): 99% of TravelDistance_Mi are about 1 mile or less…but some weird TravelDistance_Mi values (e.g., 584 miles traveled) exist.
TravDistMi_Ntile <- as.data.frame(AllDays_NewOrder$TravelDistance_Mi) %>%
mutate(#Pctile = ntile(AllDays_NewOrder$TravelDistance_Mi, 100),
#MinR = min_rank(AllDays_NewOrder$TravelDistance_Mi),
PctR = percent_rank(AllDays_NewOrder$TravelDistance_Mi),
PctR_Round = round(PctR, 2)
)
colnames(TravDistMi_Ntile)[1] <- "TravelDistance_Mi"
# str(TravDistMi_Ntile)
TravDistMi_Ntile_Rows <- nrow(TravDistMi_Ntile)
# View(tail(TravDistMi_Ntile, 500))
TravDistMi_Pctiles <- group_by(TravDistMi_Ntile,
PctR_Round
) %>%
summarise(
MinTravDistMiAtPctile = min(TravelDistance_Mi),
CntsAtPctile = n(),
PctsAtPctile = CntsAtPctile / TravDistMi_Ntile_Rows
) %>%
mutate(CumSumPAtP = cumsum(PctsAtPctile)
)
rm(TravDistMi_Ntile)
rm(TravDistMi_Ntile_Rows)
View(TravDistMi_Pctiles)
TravDistMi_Pctiles
Investigation of TravelDistance_Mi.
Why are some TravelDistance_Mi “NA”? It looks like partially because the records are the first trip of the day (for that bus), so I purposefully set the distance to “NA”. Another reason is due to the odometer recording a value less than the previous odometer recording. In most cases, I have no explanation for this - though I have observed about 67% of all instances where TravelDistance_Mi is NA (other than because it’s the first record of the day) are instances where DirChange2 is “Change”. This is weird and should be asked to WMATA.
# View(head(AllDays_NewOrder, 500))
View(filter(AllDays_NewOrder,
BusDay_EventNum != 1 # When BusDay_EventNum == 1, TravelDistance_Mi is NA by design (don't want to calculate distance based on yesterday's position)
) %>%
group_by(StartStop_ID) %>%
summarise(Cnts = sum(is.na(TravelDistance_Mi)
)
) %>%
arrange(desc(Cnts)
)
)
View(filter(AllDays_NewOrder,
StartStop_ID == "1000245--1000211"
) %>%
select(RowNum_OG,
StartStop_ID,
Event_Time,
Event_Time_HrGroup,
Bus_ID,
TravelDistance_Mi,
TravelDistance_Mi_Hvrs,
TD_Mi_SS_Mean,
TD_Mi_SS_Mean_F,
TD_Mi_SSHG_Mean,
TD_Mi_SSHG_Mean_F,
TD_Mi_SS_Med,
TD_Mi_SS_Med_F,
TD_Mi_SSHG_Med,
TD_Mi_SSHG_Med_F,
TD_Mi_SS_Cnt,
TD_Mi_SS_Cnt_F,
TD_Mi_SSHG_Cnt,
TD_Mi_SSHG_Cnt_F
) %>%
mutate(Ratio_MeanToHvrs = TD_Mi_SS_Mean / TravelDistance_Mi_Hvrs) %>%
arrange(Event_Time)
)
View(filter(AllDays_NewOrder,
is.na(TravelDistance_Mi)
)
)
# These records are NA becuase the record is the first record of the day (the Event_Time_Date)
View(filter(AllDays_NewOrder,
between(RowNum_OG, 326, 346) | # 336
between(RowNum_OG, 591, 611) | # 601
between(RowNum_OG, 845, 865) # 855
)
)
Investigation of TravelDistance_Mi.
These records are NA becuase the current record odometer is less than the previous record odometer. Theoretically, this should NOT happen. Me: it appears that about 67% of all instances where TravelDistance_Mi is NA (other than because it’s th first record of the day) are instances where DirChange2 is “Change”. This is weird and should be asked to WMATA.
View(filter(AllDays_NewOrder,
between(RowNum_OG, 194, 214) | # 204
between(RowNum_OG, 440, 460) | # 450
between(RowNum_OG, 478, 498) | # 488
between(RowNum_OG, 510, 530) # 520
)
)
TestTable <- filter(AllDays_NewOrder,
BusDay_EventNum != 1
) %>%
mutate(TravelDistance_NA = as.factor(ifelse(is.na(TravelDistance_Mi),
"True",
"False"
)
)
) %>%
group_by(DirChange2, TravelDistance_NA) %>%
summarise(TravDistMi_NACnts = n()
)
# TestTable
TestTable_Spread <- as.data.frame(spread(TestTable,
TravelDistance_NA,
TravDistMi_NACnts
)
) %>%
select(False,
True
)
row.names(TestTable_Spread) <- c("Change", "Same")
# str(TestTable_Spread)
# TestTable_Spread
prop.table(as.table(as.matrix(TestTable_Spread)
),
1
)
prop.table(as.table(as.matrix(TestTable_Spread)
),
2
)
Investigation of TravelDistance_Mi.
Let’s look at just the TravelDistance_Mi values that are NOT “NA”.
rm(TestTable, TestTable_Spread)
TravelDistance_Mi_NoNA <- filter(AllDays_NewOrder,
# TravelDistance_Mi != 0 &
!is.na(TravelDistance_Mi)
)
dim(AllDays_NewOrder)
dim(TravelDistance_Mi_NoNA)
nrow(AllDays_NewOrder) - nrow(TravelDistance_Mi_NoNA)
str(TravelDistance_Mi_NoNA)
summary(TravelDistance_Mi_NoNA)
Investigation of TravelDistance_Mi.
Let’s plot just the TravelDistance_Mi values that are NOT “NA”.
TravDistMi_HistDen <- ggplot(select(TravelDistance_Mi_NoNA,
TravelDistance_Mi
),
aes(x = TravelDistance_Mi,
y = ..density..
)
) +
geom_histogram(binwidth = 0.05, fill = "lightblue", colour = "grey60", size = 0.2) +
geom_line(stat = "density", colour = "red") +
coord_cartesian(xlim = c(0, 1.5), ylim = c(0, 4.0)
) +
labs(title = "Variation in Distance Between Stops",
x = "Travel Distance (miles)",
y = "Density"
)
TravDistMi_HistDen
Investigation of TravelDistance_Mi.
Looking at the extremely large TravelDistance_Mi values. Some (aprox 27%) of TravelDistance_Mi values > 1 mile are when the DirChange2 changes…but what about the other ~73%?
rm(TravelDistance_Mi_NoNA)
# examples of weirdly large TravelDistance_Mi
View(filter(AllDays_NewOrder,
TravelDistance_Mi > 1.1587121212 # 1.1587121212 is the 99th percentile
) %>%
arrange(desc(TravelDistance_Mi)
)
)
# Why are these extremes? Airports? Bus collection points?
View(filter(AllDays_NewOrder,
between(RowNum_OG, 494044, 494064) | # 494054
between(RowNum_OG, 494273, 494293) | # 494283
between(RowNum_OG, 494626, 494646) | # 494636
between(RowNum_OG, 1610156, 1610176) | # 1610166
between(RowNum_OG, 2073074, 2073094) # 2073084
)
)
# Before Removing Runs
# View(filter(AllDays_Sorted,
# between(RowNum_OG, 494044, 494064) | # 494054
# between(RowNum_OG, 494273, 494293) | # 494283
# between(RowNum_OG, 494626, 494646) | # 494636
# between(RowNum_OG, 1610156, 1610176) | # 1610166
# between(RowNum_OG, 2073074, 2073094) # 2073084
# )
# )
# After Removing Runs
# View(filter(AllDays_FirstStopID,
# between(RowNum_OG, 494044, 494064) | # 494054
# between(RowNum_OG, 494273, 494293) | # 494283
# between(RowNum_OG, 494626, 494646) | # 494636
# between(RowNum_OG, 1610156, 1610176) | # 1610166
# between(RowNum_OG, 2073074, 2073094) # 2073084
# )
# )
Investigation of TravelDistance_Mi.
Any relation with DirChange2? Doesn’t look as if this is so.
ExtremeTravDist <- filter(AllDays_NewOrder,
!is.na(TravelDistance_Mi)
) %>%
mutate(TravDist_Extreme = ifelse(TravelDistance_Mi > 1.1587121212, # 1.1587121212 is the 99th percentile
"True",
"False"
)
) %>%
group_by(DirChange2, TravDist_Extreme) %>%
summarise(TravDistMI_ExtCnts = n()
)
# ExtremeTravDist
ExtremeTravDist_Spread <- as.data.frame(spread(ExtremeTravDist,
TravDist_Extreme,
TravDistMI_ExtCnts
)
) %>%
select(False,
True
)
row.names(ExtremeTravDist_Spread) <- c("Change", "Same")
# str(ExtremeTravDist_Spread)
# ExtremeTravDist_Spread
prop.table(as.table(as.matrix(ExtremeTravDist_Spread)
),
1
)
prop.table(as.table(as.matrix(ExtremeTravDist_Spread)
),
2
)
Investigation of TravelDistance_Mi.
Looking at specific buses and StartStop_ID.
rm(ExtremeTravDist, ExtremeTravDist_Spread)
View(arrange(group_by(AllDays_NewOrder,
Bus_ID
) %>%
summarise(DistTrav_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
DistTrav_Med = median(TravelDistance_Mi, na.rm = TRUE)
),
desc(DistTrav_Med)
)
)
# example of extremely small TravelDistance_Mi values (looks like the odometer wasn't functioning)
View(filter(AllDays_NewOrder,
Bus_ID == 6111 |
Bus_ID == 7201 |
Bus_ID == 8058
) %>%
arrange(Bus_ID, Event_Time)
)
View(arrange(group_by(AllDays_NewOrder,
StartStop_ID
) %>%
summarise(DistTrav_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
DistTrav_Med = median(TravelDistance_Mi, na.rm = TRUE)
),
desc(DistTrav_Med)
)
)
# example of extremely large TravelDistance_Mi values...no idea why...
View(filter(AllDays_NewOrder,
StartStop_ID == "1003665--12" |
StartStop_ID == "1003665--5001925" |
StartStop_ID == "3001038--3002565"
) %>%
arrange(StartStop_ID, Event_Time)
)
Investigation of TravelDistance_Mi & TravelDistance_Mi_New.
If TravelDisntace_Mi is below the 5th percentile for that StartStop_ID, or if TravelDisntace_Mi is above the 95th percentile for that StartStop_ID, or if TravelDistance_Mi is NA (when the BusDay_EventNum !=1), consider this an outlier. In this case, replace the value with the mean for that StartStop_ID and HourGroup (TD_Mi_SSHG_Mean_F), or if there are not enough values at the HourGroup level, replace it with the mean for that StartStop_ID.
# View(tail(AllDays_NewOrder, 500))
AllDays_NewTravelDist <-
mutate(AllDays_NewOrder,
TravelDistance_Mi_New = ifelse(!is.na(TravelDistance_Mi) &
(TravelDistance_Mi < TD_Mi_SSHG_q5 |
TravelDistance_Mi > TD_Mi_SSHG_q95
) &
TD_Mi_SSHG_Cnt_F >= 20,
TD_Mi_SSHG_Mean_F,
ifelse(!is.na(TravelDistance_Mi) &
(TravelDistance_Mi < TD_Mi_SSHG_q5 |
TravelDistance_Mi > TD_Mi_SSHG_q95
) &
TD_Mi_SSHG_Cnt_F < 20 &
TD_Mi_SS_Cnt_F >= 20,
TD_Mi_SS_Mean_F,
ifelse(!is.na(TravelDistance_Mi) &
(TravelDistance_Mi < TD_Mi_SSHG_q5 |
TravelDistance_Mi > TD_Mi_SSHG_q95
) &
TD_Mi_SS_Cnt_F < 20 &
TD_Mi_SS_Cnt >= 20,
TD_Mi_SS_Mean,
ifelse(is.na(TravelDistance_Mi) &
BusDay_EventNum != 1 &
TravelDistance_Mi_Hvrs != 0,
TravelDistance_Mi_Hvrs,
ifelse(is.na(TravelDistance_Mi) &
BusDay_EventNum != 1 &
TravelDistance_Mi_Hvrs == 0,
TD_Mi_SS_Mean,
TravelDistance_Mi
))))),
TravelDistance_Mi_New_Label =
factor(ifelse(!is.na(TravelDistance_Mi) &
(TravelDistance_Mi < TD_Mi_SSHG_q5 |
TravelDistance_Mi > TD_Mi_SSHG_q95
) &
TD_Mi_SSHG_Cnt_F >= 20,
"TD_Mi_SSHG_Mean_F",
ifelse(!is.na(TravelDistance_Mi) &
(TravelDistance_Mi < TD_Mi_SSHG_q5 |
TravelDistance_Mi > TD_Mi_SSHG_q95
) &
TD_Mi_SSHG_Cnt_F < 20 &
TD_Mi_SS_Cnt_F >= 20,
"TD_Mi_SS_Mean_F",
ifelse(!is.na(TravelDistance_Mi) &
(TravelDistance_Mi < TD_Mi_SSHG_q5 |
TravelDistance_Mi > TD_Mi_SSHG_q95
) &
TD_Mi_SS_Cnt_F < 20 &
TD_Mi_SS_Cnt >= 20,
"TD_Mi_SS_Mean",
ifelse(is.na(TravelDistance_Mi) &
BusDay_EventNum != 1 &
TravelDistance_Mi_Hvrs != 0,
"TravelDistance_Mi_Hvrs",
ifelse(is.na(TravelDistance_Mi) &
BusDay_EventNum != 1 &
TravelDistance_Mi_Hvrs == 0,
"TD_Mi_SS_Mean",
"TravelDistance_Mi"
)))))
),
TravelDistance_Mi_NewHvrs = ifelse(!is.na(TravelDistance_Mi_Hvrs) &
TravelDistance_Mi_Hvrs != 0 &
(TravelDistance_Mi_New < TD_Mi_q2 |
TravelDistance_Mi_New > TD_Mi_q98
),
TravelDistance_Mi_Hvrs,
TravelDistance_Mi_New
),
TravelDistance_Mi_NewHvrs_Label =
factor(ifelse(!is.na(TravelDistance_Mi_Hvrs) &
TravelDistance_Mi_Hvrs != 0 &
(TravelDistance_Mi_New < TD_Mi_q2 |
TravelDistance_Mi_New > TD_Mi_q98
),
"TravelDistance_Mi_Hvrs",
as.character(TravelDistance_Mi_New_Label)
)
),
SpeedAvg_Mph_NewHvrs = TravelDistance_Mi_NewHvrs / TravelTime_Hr
)
rm(AllDays_NewOrder)
str(AllDays_NewTravelDist)
Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.
Quick summary and then correlation calculation.
# 38 rows meet this criteria anymore -- appears to be the case when both the Lat Long calculations, and the TravelDistance calculations did not function properly.
View(filter(AllDays_NewTravelDist,
is.na(TravelDistance_Mi_New) &
BusDay_EventNum != 1
)
)
View(AllDays_NewTravelDist %>%
arrange(desc(TravelDistance_Mi_New)) %>%
head(500)
)
summary(select(AllDays_NewTravelDist,
TravelDistance_Mi,
TravelDistance_Mi_Hvrs,
TravelDistance_Mi_New,
TravelDistance_Mi_NewHvrs
)
)
summary(select(filter(AllDays_NewTravelDist,
BusDay_EventNum != 1
),
TravelDistance_Mi,
TravelDistance_Mi_Hvrs,
TravelDistance_Mi_New,
TravelDistance_Mi_NewHvrs
)
)
cor(select(AllDays_NewTravelDist,
TravelDistance_Mi,
TravelDistance_Mi_Hvrs,
TravelDistance_Mi_New,
TravelDistance_Mi_NewHvrs
),
use = "pairwise.complete.obs"
)
Investigation of TravelDistance_Mi_NewHvrs_Label & TravelDistance_Mi_NewHvrs_Label.
Show how the labels changed.
group_by(AllDays_NewTravelDist,
TravelDistance_Mi_New_Label,
TravelDistance_Mi_NewHvrs_Label
) %>%
summarise(CntNum = n(),
CntPct = format(CntNum / nrow(AllDays_NewTravelDist),
scientific = 9999
)
) %>%
arrange(desc(CntPct)
)
Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.
Graphing the two methods of calculating TravelDistance_Mi.
First, let’s get create a function to plot the liner model equation.
lm_eqn <- function(df, y, x){
m <- lm(y ~ x, df)
l <- list(a = format(coef(m)[1], digits = 2),
b = format(abs(coef(m)[2]), digits = 2),
s1 = ifelse(test = coef(m)[2] > 0,
yes = "+",
no = "-"
),
r2 = format(summary(m)$r.squared,
digits = 3
)
)
eq <- substitute(italic(y) == a~~s1~~b %.% italic(x)*","~~italic(r)^2~"="~r2,
l
)
as.character(as.expression(eq)
)
}
Investigation of TravelDistance_Mi & TravelDistance_Mi_NewHvrs.
Scatter plot (using a 10% sample to making plotting time faster and to reduce un-needed data in the “same” splot).
set.seed(123456789)
AllDays_NewTravelDist_10Pct <- filter(AllDays_NewTravelDist,
!is.na(TravelDistance_Mi_NewHvrs) &
!is.na(TravelDistance_Mi)
) %>%
rename(DistMethod = TravelDistance_Mi_NewHvrs_Label) %>%
sample_frac(0.1)
TravDist_MiVsCalc <- ggplot(select(AllDays_NewTravelDist_10Pct,
TravelDistance_Mi_NewHvrs,
TravelDistance_Mi,
DistMethod
),
aes(x = TravelDistance_Mi,
y = TravelDistance_Mi_NewHvrs,
colour = DistMethod
)
) +
scale_colour_manual(values = c("red","blue", "green", "orange", "black")
) +
geom_point(shape = 1, alpha = 0.5) +
scale_shape(solid = FALSE) +
geom_smooth(method = "lm", colour = "blue") +
geom_abline(intercept = 0, slope = 1, colour = "red") +
coord_cartesian(xlim = c(0, 1.5), ylim = c(0, 1.5)
) +
scale_x_continuous(breaks = seq(0, 1.5, 0.25)
) +
scale_y_continuous(breaks = seq(0, 1.5, 0.25)
) +
theme(legend.position = "bottom", #c(0.85, 0.40),
legend.text = element_text(size = 6)
) +
annotate(label = lm_eqn(df = AllDays_NewTravelDist_10Pct,
x = AllDays_NewTravelDist_10Pct$TravelDistance_Mi,
y = AllDays_NewTravelDist_10Pct$TravelDistance_Mi_NewHvrs
),
# x = 62,
# y = 20,
x = 0.70,
y = 0.00,
geom = "text",
size = 3,
colour = "blue",
parse = TRUE
) +
annotate(label = "Reference Line (slope = 1)",
# x = 16,
# y = 30,
x = 0.80,
y = 1.05,
geom = "text",
size = 3,
colour = "red"
) +
labs(title = "TravelDistance_Mi vs. TravelDistance_Mi_NewHvrs",
x = "TravelDistance_Mi",
y = "TravelDistance_Mi_NewHvrs"
)
# +
# geom_jitter()
TravDist_MiVsCalc
Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.
Graphing test with rbokeh.
TravDist_MiVsCalc_Bokeh <- figure(data = select(AllDays_NewTravelDist_10Pct,
TravelDistance_Mi_NewHvrs,
TravelDistance_Mi,
DistMethod
),
xlim = c(0, 1.5),
ylim = c(0, 1.5),
legend_location = "bottom_right"
) %>%
ly_points(x = TravelDistance_Mi,
y = TravelDistance_Mi_NewHvrs,
color = DistMethod,
hover = c(TravelDistance_Mi_NewHvrs, TravelDistance_Mi, DistMethod)
) %>%
ly_abline(a = 0, b = 1, color = "red")
TravDist_MiVsCalc_Bokeh
Investigation of TravelDistance_Mi_New.
Calculating the minimum TravelDistance_Mi_New value at each percentile.
rm(TravDist_MiVsCalc_Bokeh)
rm(AllDays_NewTravelDist_10Pct)
summary(select(AllDays_NewTravelDist,
TravelDistance_Mi,
TravelDistance_Mi_Hvrs,
TravelDistance_Mi_New,
TravelDistance_Mi_NewHvrs
)
)
summary(select(filter(AllDays_NewTravelDist,
BusDay_EventNum != 1
),
TravelDistance_Mi,
TravelDistance_Mi_Hvrs,
TravelDistance_Mi_New,
TravelDistance_Mi_NewHvrs
)
)
TravDistMiN_Ntile <- as.data.frame(select(AllDays_NewTravelDist,
StartStop_ID,
TravelDistance_Mi_New_Label,
# TravelDistance_Mi_NewHvrs_Label,
TravelDistance_Mi_New
# TravelDistance_Mi_NewHvrs
)
) %>%
mutate(PctR_N = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_New),
# PctR_H = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_NewHvrs),
PctR_Round_N = round(PctR_N, 2)
# PctR_Round_H = round(PctR_H, 2)
)
# str(TravDistMiN_Ntile)
# View(head(TravDistMiN_Ntile, 500))
TravDistMiN_Ntile_Rows <- nrow(TravDistMiN_Ntile)
# View(tail(TravDistMiN_Ntile, 500))
TravDistMiN_Pctiles <- group_by(TravDistMiN_Ntile,
PctR_Round_N
) %>%
summarise(
MinTDMiAtPctile_N = min(TravelDistance_Mi_New),
# MinTDMiAtPctile_H = min(TravelDistance_Mi_NewHvrs),
CntsAtPctile_N = sum(!is.na(TravelDistance_Mi_New)),
# CntsAtPctile_H = sum(!is.na(TravelDistance_Mi_NewHvrs)),
PctsAtPctile_N = CntsAtPctile_N / TravDistMiN_Ntile_Rows
# PctsAtPctile_H = CntsAtPctile_H / TravDistMiN_Ntile_Rows
) %>%
mutate(CumSumPAtP_N = cumsum(PctsAtPctile_N)
# CumSumPAtP_H = cumsum(PctsAtPctile_H)
)
# View(TravDistMiN_Pctiles)
TravDistMiN_Pctiles
Investigation of TravelDistance_Mi_NewHvrs
Calculating the minimum TravelDistance_Mi_NewHvrs value at each percentile.
TravDistMiH_Ntile <- as.data.frame(select(AllDays_NewTravelDist,
StartStop_ID,
# TravelDistance_Mi_New_Label,
TravelDistance_Mi_NewHvrs_Label,
# TravelDistance_Mi_New,
TravelDistance_Mi_NewHvrs
)
) %>%
mutate(# PctR_N = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_New),
PctR_H = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_NewHvrs),
# PctR_Round_N = round(PctR_N, 2),
PctR_Round_H = round(PctR_H, 2)
)
# str(TravDistMiH_Ntile)
# View(head(TravDistMiH_Ntile, 500))
TravDistMiH_Ntile_Rows <- nrow(TravDistMiH_Ntile)
# View(tail(TravDistMiH_Ntile, 500))
TravDistMiH_Pctiles <- group_by(TravDistMiH_Ntile,
PctR_Round_H
) %>%
summarise(
# MinTDMiAtPctile_N = min(TravelDistance_Mi_New),
MinTDMiAtPctile_H = min(TravelDistance_Mi_NewHvrs),
# CntsAtPctile_N = sum(!is.na(TravelDistance_Mi_New)),
CntsAtPctile_H = sum(!is.na(TravelDistance_Mi_NewHvrs)),
# PctsAtPctile_N = CntsAtPctile_N / TravDistMiH_Ntile_Rows,
PctsAtPctile_H = CntsAtPctile_H / TravDistMiH_Ntile_Rows
) %>%
mutate(# CumSumPAtP_N = cumsum(PctsAtPctile_N),
CumSumPAtP_H = cumsum(PctsAtPctile_H)
)
# View(TravDistMiH_Pctiles)
TravDistMiH_Pctiles
Join TravDistMiH_Pctiles, TravDistMiN_Pctiles, and TravDistMi_Pctiles.
~11% of rides are still showing as less than 0.1 miles of TravelDistance_Mi_NewHvrs.
rm(TravDistMiN_Ntile_Rows, TravDistMiH_Ntile_Rows, TravDistMiN_Ntile, TravDistMiH_Ntile)
# View(TravDistMi_Pctiles)
# View(TravDistMiN_Pctiles)
# View(TravDistMiH_Pctiles)
TravDistMi_Pctiles_All <- inner_join(x = TravDistMi_Pctiles,
y = TravDistMiN_Pctiles,
by = c("PctR_Round" = "PctR_Round_N")
) %>%
inner_join(y = TravDistMiH_Pctiles,
by = c("PctR_Round" = "PctR_Round_H")
) %>%
select(PctR_Round,
MinTravDistMiAtPctile,
MinTDMiAtPctile_N,
MinTDMiAtPctile_H,
CntsAtPctile,
CntsAtPctile_N,
CntsAtPctile_H,
PctsAtPctile,
PctsAtPctile_N,
PctsAtPctile_H,
CumSumPAtP,
CumSumPAtP_N,
CumSumPAtP_H
)
# str(TravDistMi_Pctiles_All)
rm(TravDistMi_Pctiles, TravDistMiN_Pctiles,TravDistMiH_Pctiles)
View(TravDistMi_Pctiles_All)
TravDistMi_Pctiles_All
Investigation of TravelDistance_Mi_New.
Why are there still some small or large TravelDistance_Mi_NewHvrs values.
# View(filter(AllDays_NewTravelDist,
# !is.na(TravelDistance_Mi_NewHvrs)
# ) %>%
# select(-TD_Mi_q2,
# -TD_Mi_q98,
# -TD_Mi_SS_q5,
# -TD_Mi_SS_q95,
# -TD_Mi_SSHG_q5,
# -TD_Mi_SSHG_q95,
# -TD_Mi_Mean,
# -TD_Mi_Mean_F,
# -TD_Mi_SS_Mean,
# -TD_Mi_SS_Mean_F,
# -TD_Mi_SSHG_Mean,
# -TD_Mi_SSHG_Mean_F,
# -TD_Mi_Med,
# -TD_Mi_Med_F,
# -TD_Mi_SS_Med,
# -TD_Mi_SS_Med_F,
# -TD_Mi_SSHG_Med,
# -TD_Mi_SSHG_Med_F,
# -TD_Mi_Cnt,
# -TD_Mi_Cnt_F,
# -TD_Mi_SS_Cnt,
# -TD_Mi_SS_Cnt_F,
# -TD_Mi_SSHG_Cnt,
# -TD_Mi_SSHG_Cnt_F,
# -TT_Sec_q2,
# -TT_Sec_q98,
# -TT_Sec_SS_q5,
# -TT_Sec_SS_q95,
# -TT_Sec_SSHG_q5,
# -TT_Sec_SSHG_q95,
# -TT_Sec_Mean,
# -TT_Sec_Mean_F,
# -TT_Sec_SS_Mean,
# -TT_Sec_SS_Mean_F,
# -TT_Sec_SSHG_Mean,
# -TT_Sec_SSHG_Mean_F,
# -TT_Sec_Med,
# -TT_Sec_Med_F,
# -TT_Sec_SS_Med,
# -TT_Sec_SS_Med_F,
# -TT_Sec_SSHG_Med,
# -TT_Sec_SSHG_Med_F,
# -TT_Sec_Cnt,
# -TT_Sec_Cnt_F,
# -TT_Sec_SS_Cnt,
# -TT_Sec_SS_Cnt_F,
# -TT_Sec_SSHG_Cnt,
# -TT_Sec_SSHG_Cnt_F,
# -TT_Hr_q2,
# -TT_Hr_q98,
# -TT_Hr_SS_q5,
# -TT_Hr_SS_q95,
# -TT_Hr_SSHG_q5,
# -TT_Hr_SSHG_q95,
# -TT_Hr_Mean,
# -TT_Hr_Mean_F,
# -TT_Hr_SS_Mean,
# -TT_Hr_SS_Mean_F,
# -TT_Hr_SSHG_Mean,
# -TT_Hr_SSHG_Mean_F,
# -TT_Hr_Med,
# -TT_Hr_Med_F,
# -TT_Hr_SS_Med,
# -TT_Hr_SS_Med_F,
# -TT_Hr_SSHG_Med,
# -TT_Hr_SSHG_Med_F,
# -TT_Hr_Cnt,
# -TT_Hr_Cnt_F,
# -TT_Hr_SS_Cnt,
# -TT_Hr_SS_Cnt_F,
# -TT_Hr_SSHG_Cnt,
# -TT_Hr_SSHG_Cnt_F
# ) %>%
# arrange(TravelDistance_Mi_NewHvrs) %>%
# head(500)
# )
View(filter(AllDays_NewTravelDist,
!is.na(TravelDistance_Mi_NewHvrs)
) %>%
select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
arrange(TravelDistance_Mi_NewHvrs) %>%
head(500)
)
# examples of the smallest TravelDistance_Mi_NewHvrs values.
View(filter(AllDays_NewTravelDist,
(RowNum_OG >= 1424440 & RowNum_OG <= 1424460) | # 1424450 -- direction change
(RowNum_OG >= 763292 & RowNum_OG <= 763312) | # 763302 -- direction change
(RowNum_OG >= 1679093 & RowNum_OG <= 1679113) | # 1679103 -- direction change
(RowNum_OG >= 2860918 & RowNum_OG <= 2860938) # 2860928 -- looks correct
) %>%
select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
)
)
View(filter(AllDays_NewTravelDist,
!is.na(TravelDistance_Mi_NewHvrs)
) %>%
select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
arrange(desc(TravelDistance_Mi_NewHvrs)
) %>%
head(500)
)
# examples of the largest TravelDistance_Mi_NewHvrs values.
View(filter(AllDays_NewTravelDist,
(RowNum_OG >= 1092000 & RowNum_OG <= 1092050) | # 1092030 -- direction change
(RowNum_OG >= 1609460 & RowNum_OG <= 1609480) | # 1609470 -- direction change
(RowNum_OG >= 508904 & RowNum_OG <= 508924) | # 508914 -- direction change & original StopID was bad
(RowNum_OG >= 2476345 & RowNum_OG <= 2476365) # 2476355 -- direction change
) %>%
select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
)
)
Investigation of TravelTime_Hr.
View(TravDistMi_Pctiles): 98% of TravelTime_Hr are between 7 seconds and 464 seconds (~8 minutes).
TravTimeHr_Ntile <- select(AllDays_NewTravelDist,
TravelTime_Hr
) %>%
mutate(# Pctile = ntile(AllDays_NewTravelDist$TravelTime_Hr, 100),
# MinR = min_rank(AllDays_NewTravelDist$TravelTime_Hr),
PctR = percent_rank(AllDays_NewTravelDist$TravelTime_Hr),
PctR_Round = round(PctR, 2)
)
# str(TravTimeHr_Ntile)
TravTimeHr_Ntile_Rows <- nrow(TravTimeHr_Ntile)
# View(tail(TravTimeHr_Ntile, 500))
TravTimeHr_Pctiles <- group_by(TravTimeHr_Ntile,
PctR_Round
) %>%
summarise(
MinTravTimeHrAtPctile = min(TravelTime_Hr),
CntsAtPctile = n(),
PctsAtPctile = CntsAtPctile / TravTimeHr_Ntile_Rows
) %>%
mutate(CumSumPAtP = cumsum(PctsAtPctile),
MinTravTimeSecAtPctile = MinTravTimeHrAtPctile * 3600
)
rm(TravTimeHr_Ntile_Rows)
rm(TravTimeHr_Ntile)
View(TravTimeHr_Pctiles)
TravTimeHr_Pctiles
Investigation of TravelTime_Hr.
Histogram of TravelTime_Sec.
TravTime_Sec_HistDen <- ggplot(filter(select(AllDays_NewTravelDist,
TravelTime_Sec
),
!is.na(TravelTime_Sec)
),
aes(x = TravelTime_Sec,
y = ..density..
)
) +
geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
geom_line(stat = "density", colour = "red") +
# stat_bin(binwidth = 5,
# geom = "text",
# size = 2.5,
# vjust = 1.5,
# aes(label = format(..count.., big.mark = ",")
# ),
# ) +
coord_cartesian(xlim = c(0, 180), ylim = c(0, 0.02)
) +
# theme(legend.position="none") +
labs(title = "Variation in Travel Time",
x = "Travel Time (sec)",
y = "Density"
)
TravTime_Sec_HistDen
Investigation of TravelTime_Sec.
TravelTime_Sec values are NA.
summary(AllDays_NewTravelDist$TravelTime_Sec)
View(select(AllDays_NewTravelDist,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
filter(is.na(TravelTime_Sec) &
BusDay_EventNum != 1 # TravelTime purposefully not calculated here
)
)
# examples of TravelTime_Sec values that are NA. These are NA because the Event_Time & Departure_Time readings are not accurate (i.e., the previous Departure_Time is BEFORE or EQUAL TO the current Event_Time).
View(filter(AllDays_NewTravelDist,
(RowNum_OG >= 90809 & RowNum_OG <= 90829) | # 90819
(RowNum_OG >= 90881 & RowNum_OG <= 90901) | # 90891
(RowNum_OG >= 2597066 & RowNum_OG <= 2597086) | # 2597076
(RowNum_OG >= 2613305 & RowNum_OG <= 2613325) # 2613315
) %>%
select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt"))
)
Investigation of TravelTime_Sec.
TravelTime_Sec values are extremely small.
View(select(AllDays_NewTravelDist,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
filter(!is.na(TravelTime_Sec)
) %>%
arrange(TravelTime_Sec,
desc(SpeedAvg_Mph_NewHvrs)
) %>%
head(500)
)
# examples where TravelTime_Sec is small (1 sec) and SpeedAvg_Mph_NewHvrs is large.
View(select(AllDays_NewTravelDist,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
filter((RowNum_OG >= 2217353 & RowNum_OG <= 2217373) | # 2217363
(RowNum_OG >= 3090321 & RowNum_OG <= 3090341) | # 3090331
(RowNum_OG >= 80764 & RowNum_OG <= 80784) | # 80774
(RowNum_OG >= 33840 & RowNum_OG <= 33860) # 33850
)
)
Investigation of TravelTime_Sec.
TravelTime_Sec values are extremely large.
View(select(AllDays_NewTravelDist,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
filter(!is.na(TravelTime_Sec)
) %>%
arrange(desc(TravelTime_Sec),
SpeedAvg_Mph_NewHvrs
) %>%
head(500)
)
# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(select(AllDays_NewTravelDist,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
filter((RowNum_OG >= 1007703 & RowNum_OG <= 1007723) | # 1007713
(RowNum_OG >= 2373564 & RowNum_OG <= 2373584) | # 2373574
(RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
(RowNum_OG >= 2570060 & RowNum_OG <= 2570080) # 2570070
)
)
Investigation of TravelTime_Sec.
Are large TravelTime_Sec values related to RouteChanges? Looks likely. When the Bus involves a Route “change”, there is almost twice as likely to be a case of an outlier TravelTime_Sec value (on the high side).
TTLargeRteChng <- select(AllDays_NewTravelDist,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
mutate(TT_Out = factor(ifelse(TravelTime_Sec > 464, # this is the 99th percentile
"Outlier",
"Normal"
)
)
)
# str(TTLargeRteChng)
TTLargeRteChng_Cnts <- group_by(TTLargeRteChng,
RteChange2,
TT_Out
) %>%
summarise(Cnts = n()
)
TTLargeRteChng_Spread <- as.data.frame(spread(TTLargeRteChng_Cnts,
TT_Out,
Cnts
)
) %>%
select(-RteChange2)
row.names(TTLargeRteChng_Spread) <- c("Change", "Same")
# str(TTLargeRteChng_Spread)
# When the Bus involves a Route "change", there is almost twice as likely to be a case of an outlier TravelTime_Sec value.
TTLargeRteChng_Spread
prop.table(as.table(as.matrix(TTLargeRteChng_Spread)
),
1
)
prop.table(as.table(as.matrix(TTLargeRteChng_Spread)
),
2
)
# rm(TTLargeRteChng, TTLargeRteChng_Spread)
Investigation of TravelTime_Sec.
Are large TravelTime_Sec values related to RouteChanges? Looks likely.
View(filter(TTLargeRteChng,
!is.na(TravelTime_Sec) &
RteChange2 == "Same"
) %>%
arrange(desc(TravelTime_Sec),
SpeedAvg_Mph_NewHvrs
) %>%
head(500)
)
# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(filter(TTLargeRteChng,
(RowNum_OG >= 2250290 & RowNum_OG <= 2250310) | # 2250300
(RowNum_OG >= 867717 & RowNum_OG <= 867737) | # 867727
(RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
(RowNum_OG >= 808395 & RowNum_OG <= 808415) # 808405
)
)
Investigation of TravelTime_Sec.
If TravelTime_Sec is below the 5th percentile for that StartStop_ID, or if TravelTime_Sec is above the 95th percentile for that StartStop_ID, consider this an outlier. In this case, replace the value with the mean for that StartStop_ID and HourGroup (TT_Sec_SSHG_Mean_F), or if there are not enough values at the HourGroup level, replace it with the mean for that StartStop_ID.
rm(TTLargeRteChng, TTLargeRteChng_Cnts, TTLargeRteChng_Spread)
NewTravTime <- mutate(AllDays_NewTravelDist,
TT_Sec_New = ifelse(!is.na(TravelTime_Sec) &
(TravelTime_Sec < TT_Sec_SSHG_q5 |
TravelTime_Sec > TT_Sec_SSHG_q95
) &
TT_Sec_SSHG_Cnt_F >= 20,
TT_Sec_SSHG_Mean_F,
ifelse(!is.na(TravelTime_Sec) &
(TravelTime_Sec < TT_Sec_SSHG_q5 |
TravelTime_Sec > TT_Sec_SSHG_q95
) &
TT_Sec_SSHG_Cnt_F < 20 &
TT_Sec_SS_Cnt_F >= 20,
TT_Sec_SS_Mean_F,
ifelse(!is.na(TravelTime_Sec) &
(TravelTime_Sec < TT_Sec_SSHG_q5 |
TravelTime_Sec > TT_Sec_SSHG_q95
) &
TT_Sec_SS_Cnt_F < 20 &
TT_Sec_SS_Cnt >= 20,
TT_Sec_SS_Mean,
ifelse(!is.na(TravelTime_Sec) &
(TravelTime_Sec < TT_Sec_SSHG_q5 |
TravelTime_Sec > TT_Sec_SSHG_q95
) &
TT_Sec_SS_Cnt_F < 20 &
TT_Sec_SS_Cnt < 20 &
RteChange2 == "Change",
NA,
TravelTime_Sec
)))),
TT_Sec_New_Label =
factor(ifelse(!is.na(TravelTime_Sec) &
(TravelTime_Sec < TT_Sec_SSHG_q5 |
TravelTime_Sec > TT_Sec_SSHG_q95
) &
TT_Sec_SSHG_Cnt_F >= 20,
"TT_Sec_SSHG_Mean_F",
ifelse(!is.na(TravelTime_Sec) &
(TravelTime_Sec < TT_Sec_SSHG_q5 |
TravelTime_Sec > TT_Sec_SSHG_q95
) &
TT_Sec_SSHG_Cnt_F < 20 &
TT_Sec_SS_Cnt_F >= 20,
"TT_Sec_SS_Mean_F",
ifelse(!is.na(TravelTime_Sec) &
(TravelTime_Sec < TT_Sec_SSHG_q5 |
TravelTime_Sec > TT_Sec_SSHG_q95
) &
TT_Sec_SS_Cnt_F < 20 &
TT_Sec_SS_Cnt >= 20,
"TT_Sec_SS_Mean",
ifelse(!is.na(TravelTime_Sec) &
(TravelTime_Sec < TT_Sec_SSHG_q5 |
TravelTime_Sec > TT_Sec_SSHG_q95
) &
TT_Sec_SS_Cnt_F < 20 &
TT_Sec_SS_Cnt < 20 &
RteChange2 == "Change",
NA,
"TravelTime_Sec"
))))
),
TT_Hr_New = TT_Sec_New / (60 * 60)
)
dim(AllDays_NewTravelDist)
dim(NewTravTime)
rm(AllDays_NewTravelDist)
summary(select(NewTravTime,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
)
)
str(select(NewTravTime,
TravelTime_Sec,
TT_Sec_New,
TT_Sec_New_Label,
TT_Hr_New
)
)
summary(select(NewTravTime,
TravelTime_Sec,
TT_Sec_New,
TT_Sec_New_Label,
TT_Hr_New
)
)
Test investigation of just the X2 Route. Box plots for time between bus arrivals (by HourGroup).
View(head(select(NewTravTime,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
)
)
)
X2 <- select(NewTravTime,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
filter(Route == "X2")
str(X2)
View(head(arrange(X2,
Bus_ID,
Event_Time
),
500
)
)
X2_ByStop <- group_by(X2,
StopID_Clean
) %>%
arrange(StopID_Clean,
Event_Time) %>%
mutate(Event_Time_L1 = lag(Event_Time),
TimeToEvent_Sec = as.numeric(Event_Time - Event_Time_L1),
TimeToEvent_Min = TimeToEvent_Sec / 60
)
View(head(X2_ByStop, 500))
# Count_Values is needed to display the medians on the box plots
Count_Values <- ddply(as.data.frame(X2_ByStop),
.(Event_Time_HrGroup),
summarise,
Value_Counts = median(TimeToEvent_Min, na.rm = TRUE)
)
TimeBtwEvents_X2_BoxPlot <- ggplot(select(as.data.frame(X2_ByStop),
TimeToEvent_Min,
Event_Time_HrGroup
),
aes(factor(Event_Time_HrGroup),
TimeToEvent_Min,
fill = factor(Event_Time_HrGroup)
)
) +
geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
geom_text(data = Count_Values,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 3,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, 120)
) +
labs(title = "How Often an X2 Arrives at a Given Stop",
x = "Hour Group",
y = "Time Between Busses (min)"
)
TimeBtwEvents_X2_BoxPlot
Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Hour Group).
TimeBtwEvents_X2_ViolinPlot <- ggplot(select(as.data.frame(X2_ByStop),
TimeToEvent_Min,
Event_Time_HrGroup
),
aes(factor(Event_Time_HrGroup),
TimeToEvent_Min,
fill = factor(Event_Time_HrGroup)
)
) +
geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
trim = TRUE,
scale = "count",
na.rm = TRUE,
show.legend = NA,
inherit.aes = TRUE
) +
geom_text(data = Count_Values,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 2.5,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, 80)
) +
labs(title = "How Often an X2 Arrives at a Given Stop",
x = "Hour Group",
y = "Time Between Busses (min)"
)
TimeBtwEvents_X2_ViolinPlot
Test investigation of just the X2 Route. Box plots for time between bus arrivals (by Zip Code).
# Count_Values is needed to display the medians on the box plots
Count_Values_z <- ddply(as.data.frame(X2_ByStop),
.(Stop_Zip),
summarise,
Value_Counts = median(TimeToEvent_Min, na.rm = TRUE)
)
TimeBtwEvents_X2_BoxPlot_z <- ggplot(select(as.data.frame(X2_ByStop),
TimeToEvent_Min,
Stop_Zip
),
aes(factor(Stop_Zip),
TimeToEvent_Min,
fill = factor(Stop_Zip)
)
) +
geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
geom_text(data = Count_Values_z,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 3,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, 100)
) +
labs(title = "How Often an X2 Arrives at a Given Stop",
x = "Zip Code of Destination",
y = "Time Between Busses (min)"
)
TimeBtwEvents_X2_BoxPlot_z
Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Zip Code).
TimeBtwEvents_X2_ViolinPlot_z <- ggplot(select(as.data.frame(X2_ByStop),
TimeToEvent_Min,
Stop_Zip
),
aes(factor(Stop_Zip),
TimeToEvent_Min,
fill = factor(Stop_Zip)
)
) +
geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
trim = TRUE,
scale = "count",
na.rm = TRUE,
show.legend = NA,
inherit.aes = TRUE
) +
geom_text(data = Count_Values_z,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 2.5,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, 60)
) +
labs(title = "How Often an X2 Arrives at a Given Stop",
x = "Zip Code of Destination",
y = "Time Between Busses (min)"
)
TimeBtwEvents_X2_ViolinPlot_z
Waiting time analyses.
Munging and sampling data to go from time beteen buses to “average” waiting time.
First, get the max and min times of bus stops (each day, and for each route).
rm(X2, X2_ByStop, X2_Long, X2_Pct)
RouteMinMax <- group_by(NewTravTime,
Route,
Event_Time_Date
) %>%
summarise(MinTime = min(Event_Time),
MaxTime = max(Event_Time)
)
str(RouteMinMax)
View(RouteMinMax)
Waiting time analyses.
Munging and sampling data to go from time beteen buses to “average” waiting time.
(Pulls here are done by day, as the data are too large to do at once.)
# View(head(NewTravTime, 500))
# For each record, create a random datetime between the first and last stop for that bus route (on that day).
for(i in 3:7){
set.seed(123456789)
Samp <- select(NewTravTime,
RowNum_OG,
Route,
# RouteGroup,
Event_Time_Date,
StopID_Clean,
starts_with("Event")
) %>%
filter(Event_Time_Date == i) %>% # needed to do this each day (3-7) because the complete file was too large to do at once
left_join(RouteMinMax,
by = c("Route" = "Route",
"Event_Time_Date" = "Event_Time_Date"
)
) %>%
mutate(SampTime = as_datetime(runif(nrow(.), #200000,
min = MinTime,
max = MaxTime
),
tz = "America/New_York"
)
) %>%
arrange(Route,
StopID_Clean,
Event_Time
)
# str(Samp)
# View(head(Samp, 500))
#
# View(
# group_by(Samp,
# RowNum_OG
# ) %>%
# summarise(Cnt_Num = n(),
# Cnt_Pct = 100 * Cnt_Num / nrow(Samp)
# ) %>%
# arrange(desc(Cnt_Num))
# )
# For each Route and StopID combination, get all the Event_Time values that are after the SampTime value.
# estimating approx 2hrs of runtime for all 2.8m records
Testing_A <- sqldf(" Select t1.*
,t2.Event_Time as NextBus
From Samp as t1
Inner Join Samp as t2
On t1.Route = t2.Route
And t1.StopID_Clean = t2.StopID_Clean
And t2.Event_Time > t1.SampTime
Order By t1.Route
,t1.StopID_Clean
,t1.Event_Time
,t2.Event_Time
"
) %>%
mutate(NB = as_datetime(NextBus,
tz = "America/New_York"
)
)
# str(Testing_A)
# View(head(Testing_A, 500))
# View(head(Samp, 500))
# Filter the dataframe to only include the bus arrival at StopID that is the next to come after the SampTime.
# estimating approx 20min of runtime for all 2.8m records
Testing <- select(Testing_A,
-NextBus
) %>%
group_by(RowNum_OG) %>%
filter(NB == min(NB)
) %>%
arrange(Route,
StopID_Clean,
Event_Time
) %>%
mutate(WaitTime_Min = as.numeric(NB - SampTime),
WaitTime_Sec = WaitTime_Min * 60,
WaitTime_Sec2 = NB - SampTime,
WaitTime_Min2 = WaitTime_Sec2 / 60
) %>%
as.data.frame()
assign(paste0("Testing_", i),
Testing
)
rm(Samp,Testing_A, Testing)
str(get(paste0("Testing_", i)))
View(get(paste0("Testing_", i)))
}
# Bind all the individual dataframes together.
WaitData_DayPull <- bind_rows(Testing_3,
Testing_4,
Testing_5,
Testing_6,
Testing_7
) %>%
mutate(WaitTime_Sec3 = NB - SampTime,
WaitTime_Min3 = WaitTime_Sec3 / 60
) %>%
arrange(Route,
StopID_Clean,
Event_Time
)
rm(Testing_3, Testing_4, Testing_5, Testing_6, Testing_7)
str(WaitData_DayPull)
View(head(WaitData_DayPull, 500))
View(tail(WaitData_DayPull, 500))
Waiting time analyses.
Munging and sampling data to go from time beteen buses to “average” waiting time.
Basic investigation of any missing rows from data pulled by day.
DistinctRowNum_OG <- distinct(select(WaitData_DayPull,
RowNum_OG
)
)
str(DistinctRowNum_OG)
# View(
# anti_join(Samp,
# DistinctRowNum_OG,
# by = c("RowNum_OG" = "RowNum_OG")
# )
# )
# The samp time is AFTER the last bus passed that StopID_Clean
# View(filter(Samp,
# Event_Time > "2016-10-07 19:48:41" &
# Route == "X2" &
# StopID_Clean == 1003774
# )
# )
# Next Bus (NB) can be on the next morning
# View(filter(Testing7,
# SampTime > "2016-10-06 23:58:00" &
# SampTime < "2016-10-06 23:59:59")
# )
Waiting time analyses.
Munging and sampling data to go from time beteen buses to “average” waiting time.
(Pulls here are done by groupings of bus routes, as the data are too large to do at once.)
First, we need to find the most common bus routes.
rm(DistinctRowNum_OG)
# View(head(NewTravTime, 500))
set.seed(123456789)
BusGroups <- group_by(NewTravTime,
Route
) %>%
summarise(Cnt_Num = n(),
Cnt_Pct = Cnt_Num / nrow(NewTravTime)
) %>%
arrange(desc(Cnt_Num)
) %>%
mutate(RowNum = row_number(),
RandNum = runif(n = 268),
RouteGroup = ifelse(RandNum <= 0.2,
1,
ifelse(RandNum <= 0.4,
2,
ifelse(RandNum <= 0.6,
3,
ifelse(RandNum <= 0.8,
4,
5
))))
)
str(BusGroups)
View(BusGroups)
summary(BusGroups)
Waiting time analyses.
Munging and sampling data to go from time beteen buses to “average” waiting time.
(Pulls here are done by groupings of bus routes, as the data are too large to do at once.)
# View(head(NewTravTime, 500))
# For each record, create a random datetime between the first and last stop for that bus route (on that day).
for(i in 1:5){
set.seed(123456789)
Samp <- left_join(NewTravTime,
BusGroups,
by = c("Route" = "Route")
) %>%
select(RowNum_OG,
Route,
RouteGroup,
Event_Time_Date,
StopID_Clean,
starts_with("Event")
) %>%
filter(RouteGroup == i) %>% # needed to do this each RouteGroup (1-5) because the complete file was too large to do at once
left_join(RouteMinMax,
by = c("Route" = "Route",
"Event_Time_Date" = "Event_Time_Date"
)
) %>%
mutate(SampTime = as_datetime(runif(nrow(.), #200000,
min = MinTime,
max = MaxTime
),
tz = "America/New_York"
)
) %>%
arrange(Route,
StopID_Clean,
Event_Time
)
# str(Samp)
# View(head(Samp, 500))
#
# View(
# group_by(Samp,
# RowNum_OG
# ) %>%
# summarise(Cnt_Num = n(),
# Cnt_Pct = 100 * Cnt_Num / nrow(Samp)
# ) %>%
# arrange(desc(Cnt_Num))
# )
# For each Route and StopID combination, get all the Event_Time values that are after the SampTime value.
# estimating approx 2hrs of runtime for all 2.8m records
Testing_A <- sqldf(" Select t1.*
,t2.Event_Time as NextBus
From Samp as t1
Inner Join Samp as t2
On t1.Route = t2.Route
And t1.StopID_Clean = t2.StopID_Clean
And t2.Event_Time > t1.SampTime
Order By t1.Route
,t1.StopID_Clean
,t1.Event_Time
,t2.Event_Time
"
) %>%
mutate(NB = as_datetime(NextBus,
tz = "America/New_York"
)
)
# str(Testing_A)
# View(head(Testing_A, 500))
# View(head(Samp, 500))
# Filter the dataframe to only include the bus arrival at StopID that is the next to come after the SampTime.
# estimating approx 20min of runtime for all 2.8m records
Testing <- select(Testing_A,
-NextBus
) %>%
group_by(RowNum_OG) %>%
filter(NB == min(NB)
) %>%
arrange(Route,
StopID_Clean,
Event_Time
) %>%
mutate(WaitTime_Min = as.numeric(NB - SampTime),
WaitTime_Sec = WaitTime_Min * 60
) %>%
as.data.frame()
assign(paste0("Testing", i),
Testing
)
rm(Samp,Testing_A, Testing)
str(get(paste0("Testing", i)))
View(get(paste0("Testing", i)))
}
# Bind all the individual dataframes together.
WaitData_RoutePull <- bind_rows(Testing1,
Testing2,
Testing3,
Testing4,
Testing5
) %>%
mutate(WaitTime_Sec2 = NB - SampTime,
WaitTime_Min2 = WaitTime_Sec2 / 60
) %>%
arrange(Route,
StopID_Clean,
Event_Time
)
rm(BusGroups, i, Testing3, Testing4, Testing5, Testing6, Testing7)
str(WaitData_RoutePull)
View(head(WaitData_RoutePull, 500))
View(tail(WaitData_RoutePull, 500))
Waiting time analyses.
Munging and sampling data to go from time beteen buses to “average” waiting time.
Compare WaitData pulled by day and pulled by route.
dim(WaitData_RoutePull)
dim(WaitData_DayPull)
nrow(WaitData_RoutePull) - nrow(WaitData_DayPull)
WaitData_Diff <- anti_join(WaitData_RoutePull,
WaitData_DayPull,
by = c("RowNum_OG" = "RowNum_OG"
)
) %>%
select(-WaitTime_Min,
-WaitTime_Sec
)
str(WaitData_Diff)
View(head(WaitData_Diff, 500))
View(filter(WaitData_RoutePull,
Route == "Z8" &
StopID_Clean == 2005465
# RowNum_OG = 2902760
# Event_Time = 2016-10-07 19:51:47
)
)
View(group_by(WaitData_Diff,
Route
) %>%
summarise(Cnt_Num = n(),
Cnt_Pct = Cnt_Num / nrow(WaitData_Diff)
) %>%
arrange(desc(Cnt_Num)
)
)
View(filter(WaitData_Diff,
Route == "S1"
)
)
View(filter(WaitData_RoutePull,
Route == "S1" &
StopID_Clean == 1003132
# RowNum_OG = 1151770
# Event_Time = 2016-10-07 09:07:12
)
)
# Can't tell why the pull by day has less records than the pull by route
Waiting time analyses.
Munging and sampling data to go from time beteen buses to “average” waiting time.
Compare WaitData (pulled by route) and original data (NewTravTime).
dim(NewTravTime) # 2,809,529 rows
dim(WaitData_RoutePull) # 2,780,848 rows
nrow(NewTravTime) - nrow(WaitData_RoutePull) # is 28,681 rows
str(select(NewTravTime,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
)
)
str(WaitData_RoutePull)
Compare_NTT_WD <- left_join(NewTravTime,
select(WaitData_RoutePull,
RowNum_OG,
# Route,
RouteGroup,
# StopID_Clean,
# Event_Time,
MinTime,
MaxTime,
SampTime,
NB,
WaitTime_Sec2,
WaitTime_Min2
),
by = c("RowNum_OG" = "RowNum_OG")
) %>%
select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
) %>%
arrange(Route,
StopID_Clean,
Event_Time
)
str(Compare_NTT_WD) # 2,810,109 rows overall -- 29,261 rows with no match
View(head(Compare_NTT_WD, 500))
View(filter(Compare_NTT_WD,
is.na(MinTime)
)
)
# View(anti_join(Samp,
# distinct(select(WaitData_RoutePull,
# RowNum_OG
# )
# ),
# by = c("RowNum_OG" = "RowNum_OG")
# )
# )
# The SampTime is AFTER the last bus passed that StopID_Clean
# View(filter(Samp,
# Route == "X2" &
# StopID_Clean == 1003774
# # RowNum_OG = 1146723
# # Event_Time = 2016-10-07 15:32:18
# )
# )
Clean up the data a bit.
rm(BusGroups, RouteMinMax, Samp, Testing1, Testing2, Testing3, Testing4, Testing5, Testing_3, Testing_4, Testing_5, Testing_6, Testing_7, WaitData_DayPull, WaitData_Diff)
str(Compare_NTT_WD)
View(head(Compare_NTT_WD, 500))
View(head(mutate(Compare_NTT_WD,
WT_Min = as.numeric(WaitTime_Min2)
)
)
)
WaitTime_AsNum <- Compare_NTT_WD %>%
mutate(RouteStop_ID = factor(paste(Route, StopID_Clean, sep = "__")
)
)
WaitTime_AsNum$WaitTime_Sec2 <- as.numeric(WaitTime_AsNum$WaitTime_Sec2)
WaitTime_AsNum$WaitTime_Min2 <- as.numeric(WaitTime_AsNum$WaitTime_Min2)
rm(Compare_NTT_WD)
str(WaitTime_AsNum)
General exploration of wait times.
summary(WaitTime_AsNum$WaitTime_Min2)
General exploration of wait times.
WT_Quantiles <- as.data.frame(quantile(WaitTime_AsNum$WaitTime_Min2,
probs = seq(0, 1, 0.01),
na.rm = TRUE
)
)
colnames(WT_Quantiles) <- "Value_Min"
WT_Quantiles$Value_Sec = format(round(WT_Quantiles$Value_Min * 60,
digits = 2
),
nsmall = 2
)
WT_Quantiles$Value_Hr = format(round(WT_Quantiles$Value_Min / 60,
digits = 2
),
nsmall = 2
)
WT_Quantiles$Value_Min = format(round(WT_Quantiles$Value_Min,
digits = 2
),
nsmall = 2
)
WT_Quantiles$Quantile <- seq(0, 1, 0.01)
WT_Quantiles <- select(WT_Quantiles,
Quantile,
Value_Sec,
Value_Min,
Value_Hr
)
str(WT_Quantiles)
View(WT_Quantiles)
WT_Quantiles
View(arrange(WaitTime_AsNum,
desc(WaitTime_Min2)
) %>%
head(., 5000)
)
View(filter(WaitTime_AsNum,
between(WaitTime_Min2, 60, 200)
) %>%
arrange(desc(WaitTime_Min2)
)
# %>%
# head(., 5000)
)
# Example of extreme wait times
View(filter(WaitTime_AsNum,
Route == "W13" & # only 2 bus passes in the entire dataset
StopID_Clean == 1003728
# Event_Time = 2016-10-03 08:42:46
)
)
# Example of extreme wait times
View(filter(WaitTime_AsNum,
Route == "S41" & # only 4 bus passes in the entire dataset
StopID_Clean == 1001095
# Event_Time = 2016-10-05 15:41:47
)
)
# Example of extreme wait times
View(filter(WaitTime_AsNum,
Route == "D8" & # route has VERY limited service after midnight
StopID_Clean == 1001669
# Event_Time = 2016-10-06 20:31:16
)
)
Looks like there might be an issue in wait times when very few Route-Stop combinations are included in the dataset. Let’s explore these.
RouteStop_Cnts <- group_by(WaitTime_AsNum,
RouteStop_ID
) %>%
summarise(RouteStop_CntNum = n(),
RouteStop_CntPct = RouteStop_CntNum / nrow(WaitTime_AsNum)
) %>%
arrange(RouteStop_CntNum)
View(RouteStop_Cnts)
RouteStop_CntOfCnt <- group_by(RouteStop_Cnts,
RouteStop_CntNum
) %>%
summarise(RouteStopCnt_CntNum = n(),
RouteStopCnt_CntPct = RouteStopCnt_CntNum / nrow(RouteStop_Cnts)
) %>%
mutate(RouteStopCnt_CntPct_CumSum = cumsum(RouteStopCnt_CntPct),
x = 1 - RouteStopCnt_CntPct_CumSum
) %>%
arrange(RouteStop_CntNum)
View(RouteStop_CntOfCnt)
RouteStop_CntOfCnt
Histogram of the counts of Route-StopID combinations.
RouteStop_Cnts_Bar <- ggplot(RouteStop_CntOfCnt,
aes(x = RouteStop_CntNum,
# y = ..density..
y = RouteStopCnt_CntNum
)
) +
# geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
geom_col(fill = "lightblue", colour = "grey60", size = 0.2) +
coord_cartesian(xlim = c(0, 500)
# ylim = c(0, 0.02)
) +
labs(title = "Variation in Routes Passing a Specific Stop",
x = "Occurrences of Route-StopID Combiantions",
y = "Counts"
)
RouteStop_Cnts_Bar
Create a new dataset limiting extremely small counts of Route-StopID combinations.
WaitTime_RteCnts <- left_join(WaitTime_AsNum,
RouteStop_Cnts,
by = c("RouteStop_ID" = "RouteStop_ID")
) %>%
select(-RouteStop_CntPct)
dim(WaitTime_AsNum)
dim(WaitTime_RteCnts)
rm(WaitTime_AsNum)
str(WaitTime_RteCnts)
# Total rows
nrow(WaitTime_RteCnts)
# Rows of rare RouteStops
nrow(filter(WaitTime_RteCnts,
RouteStop_CntNum <= 60
)
) / nrow(WaitTime_RteCnts)
# Rows of extremely long wait times
nrow(filter(WaitTime_RteCnts,
WaitTime_Min2 > 180
)
) / nrow(WaitTime_RteCnts)
select(WaitTime_RteCnts,
WaitTime_Min2
) %>%
summary()
filter(WaitTime_RteCnts,
RouteStop_CntNum > 60 # 12 passes per day in a 5-day dataset
) %>%
select(WaitTime_Min2) %>%
summary()
filter(WaitTime_RteCnts,
WaitTime_Min2 < 180 # probably means that something went wrong
) %>%
select(WaitTime_Min2) %>%
summary()
Compare quantiles in the limited datasets.
a <- as.data.frame(select(WaitTime_RteCnts,
WaitTime_Min2
) %>%
quantile(probs = seq(0, 1, 0.01), na.rm = TRUE)
)
b <- as.data.frame(filter(WaitTime_RteCnts,
RouteStop_CntNum > 60
) %>%
select(WaitTime_Min2) %>%
quantile(probs = seq(0, 1, 0.01), na.rm = TRUE)
)
c <- as.data.frame(filter(WaitTime_RteCnts,
WaitTime_Min2 < 180
) %>%
select(WaitTime_Min2) %>%
quantile(probs = seq(0, 1, 0.01), na.rm = TRUE)
)
WT_Filter_Quantiles <- bind_cols(a, b, c) %>%
mutate(Quantile = seq(0, 1, 0.01)
)
colnames(WT_Filter_Quantiles) <- c("All", "RteStpAbv60", "WTBlw180", "Quantile")
rm(a, b, c)
View(WT_Filter_Quantiles)
WT_Filter_Quantiles
Histogram of all wait times.
WaitTime_AllBus_HistDen <- ggplot(filter(select(WaitTime_RteCnts,
WaitTime_Min2
),
!is.na(WaitTime_Min2)
),
aes(x = WaitTime_Min2,
y = ..density..
)
) +
geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
geom_line(stat = "density", colour = "red") +
scale_x_continuous(breaks = seq(0, 300, 30)
) +
coord_cartesian(xlim = c(0, 300),
ylim = c(0, 0.035)
) +
labs(title = "Variation in Wait Time",
x = "Wait Time (min)",
y = "Density"
)
WaitTime_AllBus_HistDen
Box plots for WaitTime (all busses, by Zip Code).
# Count_Values is needed to display the medians on the box plots
BusRoute <- select(WaitTime_RteCnts,
Route,
WaitTime_Min2,
Stop_Zip
) %>%
filter(Route == "X2")
CountValues_AllBus_Zip <- ddply(BusRoute,
.(Stop_Zip),
summarise,
Value_Counts = median(WaitTime_Min2, na.rm = TRUE)
)
WaitTime_AllBus_Zip_Box <- ggplot(BusRoute,
aes(factor(Stop_Zip),
WaitTime_Min2,
fill = factor(Stop_Zip)
)
) +
geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
geom_text(data = CountValues_AllBus_Zip,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 3,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, 45)
) +
labs(title = "Waiting Time at a Given Stop (for the X2)",
x = "Zip Code of Destination",
y = "Waiting Time (min)"
)
WaitTime_AllBus_Zip_Box
Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Zip Code).
WaitTime_AllBus_Zip_Violin <- ggplot(BusRoute,
aes(factor(Stop_Zip),
WaitTime_Min2,
fill = factor(Stop_Zip)
)
) +
geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
trim = TRUE,
scale = "count",
na.rm = TRUE,
show.legend = NA,
inherit.aes = TRUE
) +
geom_text(data = CountValues_AllBus_Zip,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 3.5,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, 45)
) +
labs(title = "Waiting Time at a Given Stop (for the X2)",
x = "Zip Code of Destination",
y = "Waiting Time (min)"
)
TimeBtwEvents_X2_ViolinPlot_z
Box plots for WaitTime (Zip Code, by HourGroupZip).
# Count_Values is needed to display the medians on the box plots
Zip <- select(WaitTime_RteCnts,
Route,
WaitTime_Min2,
Stop_Zip,
Event_Time_HrGroup
) %>%
filter(Stop_Zip == 20002)
CountValues_AllBus_HG <- ddply(Zip,
.(Event_Time_HrGroup),
summarise,
Value_Counts = median(WaitTime_Min2,
na.rm = TRUE
)
)
WaitTime_AllBus_HG_Box <- ggplot(Zip,
aes(factor(Event_Time_HrGroup),
WaitTime_Min2,
fill = factor(Event_Time_HrGroup)
)
) +
geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
geom_text(data = CountValues_AllBus_HG,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 2.5,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, 45)
) +
labs(title = "Waiting Time at a Given Stop (for Zip 20002)",
x = "Hour Group",
y = "Waiting Time (min)"
)
# facet_wrap(~Stop_Zip
# # nrow = 5
# )
WaitTime_AllBus_HG_Box
Violin plots for WaitTime (Zip Code, by HourGroupZip).
WaitTime_AllBus_HG_Vln <- ggplot(Zip,
aes(factor(Event_Time_HrGroup),
WaitTime_Min2,
fill = factor(Event_Time_HrGroup)
)
) +
geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
trim = TRUE,
scale = "count",
na.rm = TRUE,
show.legend = NA,
inherit.aes = TRUE
) +
geom_text(data = CountValues_AllBus_HG,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 2.5,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, 90)
) +
labs(title = "Waiting Time at a Given Stop (for Zip 20002)",
x = "Hour Group",
y = "Waiting Time (min)"
)
# facet_wrap(~Stop_Zip
# # nrow = 5
# )
WaitTime_AllBus_HG_Vln
Box plots for WaitTime (Route, by HourGroupZip).
# Count_Values is needed to display the medians on the box plots
Rte <- select(WaitTime_RteCnts,
Route,
WaitTime_Min2,
Stop_Zip,
Event_Time_HrGroup
) %>%
filter(Route == "X2")
CountValues_AllBus_RteHG <- group_by(Rte,
Event_Time_HrGroup
) %>%
summarise(
Value_Counts = median(WaitTime_Min2,
na.rm = TRUE
),
VC = quantile(WaitTime_Min2, probs = 0.9, na.rm = TRUE)
)
WaitTime_AllBus_RteHG_Box <- ggplot(Rte,
aes(factor(Event_Time_HrGroup),
WaitTime_Min2,
fill = factor(Event_Time_HrGroup)
)
) +
geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
geom_text(data = CountValues_AllBus_RteHG,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 2.5,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, max(CountValues_AllBus_RteHG$VC))
) +
labs(title = "Waiting Time at a Given Stop",
subtitle = ("Route X2"),
x = "Hour Group",
y = "Waiting Time (min)"
)
# +
# facet_wrap(~Stop_Zip
# # nrow = 5
# )
WaitTime_AllBus_RteHG_Box
Violin plots for WaitTime (Zip Code, by HourGroupZip).
WaitTime_AllBus_RteHG_Vln <- ggplot(Rte,
aes(factor(Event_Time_HrGroup),
WaitTime_Min2,
fill = factor(Event_Time_HrGroup)
)
) +
geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
trim = TRUE,
scale = "count",
na.rm = TRUE,
show.legend = NA,
inherit.aes = TRUE
) +
geom_text(data = CountValues_AllBus_RteHG,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 2.5,
vjust = -0.5
) +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
coord_cartesian(# xlim = c(0, 180),
ylim = c(0, 45)
) +
labs(title = "Waiting Time at a Given Stop",
subtitle = ("(Route X2)"),
x = "Hour Group",
y = "Waiting Time (min)"
) +
facet_wrap(~Stop_Zip
# nrow = 5
)
WaitTime_AllBus_RteHG_Vln
X2 Percentiles Line Graph Test.
X2_Pct <- select(WaitTime_RteCnts,
Route,
Stop_Zip,
Event_Time_Date,
Event_Time_Day,
Event_Time_HrGroup,
Event_Time_Hr,
Latitude,
Longitude,
WaitTime_Min2
) %>%
filter(Route == "X2") %>%
group_by(Event_Time_Hr,
Stop_Zip
) %>%
summarise(Pct50 = quantile(WaitTime_Min2, probs = 0.5, na.rm = TRUE),
Pct60 = quantile(WaitTime_Min2, probs = 0.6, na.rm = TRUE),
Pct70 = quantile(WaitTime_Min2, probs = 0.7, na.rm = TRUE),
Pct80 = quantile(WaitTime_Min2, probs = 0.8, na.rm = TRUE),
Pct90 = quantile(WaitTime_Min2, probs = 0.9, na.rm = TRUE)
)
str(X2_Pct)
View(X2_Pct)
X2_Long <- gather(X2_Pct,
key = Percentile,
value = Pctile,
Pct50,
Pct60,
Pct70,
Pct80,
Pct90
)
str(X2_Long)
View(X2_Long)
X2_WaitByHr_Line <- ggplot(X2_Long,
aes(x = Event_Time_Hr,
y = Pctile,
factor(Percentile),
color = Percentile
)
) +
geom_line() +
theme(legend.title=element_blank(),
legend.position = "bottom"
) +
coord_cartesian(xlim = c(0, 23)
# ylim = c(0, 45)
) +
scale_x_continuous(breaks = seq(0, 23, 2)
) +
labs(title = "Waiting Time Throughout the Day",
subtitle = ("(Route X2)"),
x = "Hour of the Day",
y = "Waiting Time (min)"
) +
facet_wrap(~Stop_Zip)
X2_WaitByHr_Line
GET DATA READY FOR SHINY – GET DATA READY FOR SHINY – GET DATA READY FOR SHINY GET DATA READY FOR SHINY – GET DATA READY FOR SHINY – GET DATA READY FOR SHINY GET DATA READY FOR SHINY – GET DATA READY FOR SHINY – GET DATA READY FOR SHINY
BaseData: Used in plots by hour and zipcode (first two Shiny tabs).
# str(WaitTime_RteCnts)
Shiny_WaitData_Base <- select(WaitTime_RteCnts,
Route,
Stop_Zip,
Event_Time,
Event_Time_Date,
Event_Time_Day,
Event_Time_HrGroup,
Event_Time_Hr,
Latitude,
Longitude,
WaitTime_Min2
) %>%
mutate(Event_Time_YrMthDayHr = floor_date(Event_Time, "hour")
) %>%
rename(ZipCode = Stop_Zip,
HourGroup = Event_Time_HrGroup,
Date = Event_Time_Date,
Day = Event_Time_Day,
Hour = Event_Time_Hr,
WaitTime_Min = WaitTime_Min2
) %>%
filter(WaitTime_Min <= 180)
Shiny_WaitData_Base$Route <- factor(Shiny_WaitData_Base$Route)
str(Shiny_WaitData_Base)
View(tail(Shiny_WaitData_Base, 500))
saveRDS(Shiny_WaitData_Base,
"Shiny_WaitData_Base.rds"
)
Prep data for mapping.
# devtools::install_github("dkahle/ggmap")
# devtools::install_github("hadley/ggplot2")
# install.packages("ggmap", type = "source")
# devtools::install_github('hadley/ggplot2')
devtools::install_github("hadley/ggplot2@v2.2.0")
# devtools::install_github('thomasp85/ggforce')
# devtools::install_github('thomasp85/ggraph')
# devtools::install_github('slowkow/ggrepel')
tract <-
readOGR(dsn = "/Users/mdturse/Desktop/Analytics/DCMetroBus/tl_2016_us_zcta510",
layer = "tl_2016_us_zcta510"
)
class(tract)
# convert the GEOID to a character
tract@data$GEOID <- as.character(tract@data$GEOID)
str(tract@data)
ggtract <- tidy(tract, region = "GEOID")
# str(ggtract)
# summary(ggtract)
# View(head(ggtract, 50))
# str(Shiny_WaitData_Base)
ZipWaitTest <- filter(Shiny_WaitData_Base,
WaitTime_Min <= 180 &
!is.na(ZipCode)
) %>%
group_by(ZipCode,
Event_Time_YrMthDayHr
# Event_Time_Day,
# Event_Time_Hr
) %>%
summarise(Pct80 = quantile(WaitTime_Min, probs = 0.8, na.rm = TRUE)
) %>%
arrange(# Event_Time_Hr,
ZipCode,
Event_Time_YrMthDayHr
) %>%
as.data.frame() %>%
mutate(Event_Time_DateNew = floor_date(Event_Time_YrMthDayHr, "day"),
Event_Time_HrNew = hour(Event_Time_YrMthDayHr),
Pct80_Level = factor(ifelse(Pct80 < 10,
"Below 10",
ifelse(Pct80 < 20,
"Below 20",
ifelse(Pct80 < 30,
"Below 30",
ifelse(Pct80 < 40,
"Below 40",
ifelse(Pct80 < 50,
"Below 50",
ifelse(Pct80 < 60,
"Below 60",
"60 Plus"
)))))),
levels = c("Below 10", "Below 20", "Below 30",
"Below 40", "Below 50", "Below 60", "60 Plus"
),
ordered = TRUE
)
)
str(ZipWaitTest)
ZipWaitTest$ZipCode <- as.character(ZipWaitTest$ZipCode)
str(ZipWaitTest)
summary(ZipWaitTest)
View(head(ZipWaitTest, 500))
StopZip_Left <- left_join(ZipWaitTest,
ggtract,
by = c("ZipCode" = "id")
)
str(StopZip_Left)
summary(StopZip_Left)
Test mapping functionaltiy.
map <- get_map(location = c(lon = -77.03676, lat = 38.89784),
source = "google",
# maptype = "roadmap"
zoom = 12
)
ggmap(map) +
geom_polygon(aes(x = long,
y = lat,
group = group,
fill = Pct80_Level
),
data = filter(StopZip_Left,
Event_Time_YrMthDayHr == as.POSIXct("2016-10-07 20:00:00")
# &
# Stop_Zip == "20003"
),
colour = "gray1",
# fill = 'black',
alpha = .4,
size = .3
) +
# +
# scale_fill_gradientn(colours = c("white", "royalblue4", "red"),
# # "lightsteelblue4",
# # "lightpink1",
# # values=cbPalette,
# # values = c(1,0.5, .3, .2, .1, 0)
# na.value = "black",
# breaks = c(seq(0, 180, 30))
# # values = rescale()
# )
# +
scale_fill_brewer(palette = "Spectral", # "YlOrRd" # "Set1",
direction = -1,
limits = levels(StopZip_Left$Pct80_Level)
)
Shiny data for mapping (used in 3rd tab).
View(head(filter(StopZip_Left,
Event_Time_HrNew == 15
),
500
)
)
Shiny_WaitData_Map <- StopZip_Left %>%
rename(YrMthDayHr = Event_Time_YrMthDayHr,
YrMthDay = Event_Time_DateNew,
Hour = Event_Time_HrNew
)
str(Shiny_WaitData_Map)
Shiny_WaitData_Map_Wed <- filter(Shiny_WaitData_Map,
YrMthDay == as.POSIXct("2016-10-05")
)
str(Shiny_WaitData_Map_Wed)
summary(Shiny_WaitData_Map_Wed)
saveRDS(Shiny_WaitData_Map,
"Shiny_WaitData_Map.rds"
)
saveRDS(Shiny_WaitData_Map_Wed,
"Shiny_WaitData_Map_Wed.rds"
)
Clustering
Data prep.
rm(tract, ggtract, StopZip_Left, ZipWaitTest, Shiny_WaitData_Base, Shiny_WaitData_Map, Shiny_WaitData_Map_Wed)
dim(NewTravTime)
dim(WaitTime_RteCnts)
str(select(NewTravTime,
-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
)
)
str(select(NewTravTime,
matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
)
)
str(WaitTime_RteCnts)
# ClustData <- select(WaitTime_RteCnts,
# group,
# BusDay_EventNum,
# Route,
# RteChange2,
# RouteAlt,
# DirChange2,
# Route_Direction,
# Stop_Sequence,
# StopID_Indicator,
# Stop_County,
# Stop_City,
# Stop_Zip,
# Event_Time_Hr,
# Dwell_Time2,
# TravelDistance_Mi_NewHvrs,
# TravelDistance_Mi_NewHvrs_Label,
# TT_Sec_New,
# TT_Sec_New_Label,
# WaitTime_Min2
# ) %>%
# filter(WaitTime_Min2 <= 180) %>%
# mutate(SpeedAvg_Mph_TDMNH_TTSN = TravelDistance_Mi_NewHvrs /
# (TT_Sec_New / 60 / 60)
# )
# %>%
# select_if(ClustData,
# function(col) is.numeric(col) |
# is.integer(col)
# ) %>%
# scale()
# str(ClustData)
# View(tail(ClustData, 500))
# rownames(ClustData) <- ClustData$Route
# ClustData$Route <- as.factor(ClustData$Route)
# str(ClustData)
# head(ClustData)
RouteStats <- filter(WaitTime_RteCnts,
WaitTime_Min2 <= 180
) %>%
mutate(SpeedAvg_Mph_TDMNH_TTSN = TravelDistance_Mi_NewHvrs / (TT_Sec_New / 60 / 60)
) %>%
group_by(Route) %>%
summarise(BusDayEventNum_Mean = mean(BusDay_EventNum, na.rm = TRUE),
BusDayEventNum_Pct10 = quantile(BusDay_EventNum, probs = 0.10, na.rm = TRUE),
BusDayEventNum_Pct25 = quantile(BusDay_EventNum, probs = 0.25, na.rm = TRUE),
BusDayEventNum_Pct50 = quantile(BusDay_EventNum, probs = 0.50, na.rm = TRUE),
BusDayEventNum_Pct75 = quantile(BusDay_EventNum, probs = 0.75, na.rm = TRUE),
BusDayEventNum_Pct90 = quantile(BusDay_EventNum, probs = 0.90, na.rm = TRUE),
StopSequence_Mean = mean(Stop_Sequence, na.rm = TRUE),
StopSequence_Pct10 = quantile(Stop_Sequence, probs = 0.10, na.rm = TRUE),
StopSequence_Pct25 = quantile(Stop_Sequence, probs = 0.25, na.rm = TRUE),
StopSequence_Pct50 = quantile(Stop_Sequence, probs = 0.50, na.rm = TRUE),
StopSequence_Pct75 = quantile(Stop_Sequence, probs = 0.75, na.rm = TRUE),
StopSequence_Pct90 = quantile(Stop_Sequence, probs = 0.90, na.rm = TRUE),
EventTimeHr_Mean = mean(Event_Time_Hr, na.rm = TRUE),
EventTimeHr_Pct10 = quantile(Event_Time_Hr, probs = 0.10, na.rm = TRUE),
EventTimeHr_Pct25 = quantile(Event_Time_Hr, probs = 0.25, na.rm = TRUE),
EventTimeHr_Pct50 = quantile(Event_Time_Hr, probs = 0.50, na.rm = TRUE),
EventTimeHr_Pct75 = quantile(Event_Time_Hr, probs = 0.75, na.rm = TRUE),
EventTimeHr_Pct90 = quantile(Event_Time_Hr, probs = 0.90, na.rm = TRUE),
DwellTime2_Mean = mean(Dwell_Time2, na.rm = TRUE),
DwellTime2_Pct10 = quantile(Dwell_Time2, probs = 0.10, na.rm = TRUE),
DwellTime2_Pct25 = quantile(Dwell_Time2, probs = 0.25, na.rm = TRUE),
DwellTime2_Pct50 = quantile(Dwell_Time2, probs = 0.50, na.rm = TRUE),
DwellTime2_Pct75 = quantile(Dwell_Time2, probs = 0.75, na.rm = TRUE),
DwellTime2_Pct90 = quantile(Dwell_Time2, probs = 0.90, na.rm = TRUE),
TravDistMi_Mean = mean(TravelDistance_Mi_NewHvrs, na.rm = TRUE),
TravDistMi_Pct10 = quantile(TravelDistance_Mi_NewHvrs,
probs = 0.10, na.rm = TRUE
),
TravDistMi_Pct25 = quantile(TravelDistance_Mi_NewHvrs,
probs = 0.25, na.rm = TRUE
),
TravDistMi_Pct50 = quantile(TravelDistance_Mi_NewHvrs,
probs = 0.50, na.rm = TRUE
),
TravDistMi_Pct75 = quantile(TravelDistance_Mi_NewHvrs,
probs = 0.75, na.rm = TRUE
),
TravDistMi_Pct90 = quantile(TravelDistance_Mi_NewHvrs,
probs = 0.90, na.rm = TRUE
),
TravTimSec_Mean = mean(TT_Sec_New, na.rm = TRUE),
TravTimSec_Pct10 = quantile(TT_Sec_New, probs = 0.10, na.rm = TRUE),
TravTimSec_Pct25 = quantile(TT_Sec_New, probs = 0.25, na.rm = TRUE),
TravTimSec_Pct50 = quantile(TT_Sec_New, probs = 0.50, na.rm = TRUE),
TravTimSec_Pct75 = quantile(TT_Sec_New, probs = 0.75, na.rm = TRUE),
TravTimSec_Pct90 = quantile(TT_Sec_New, probs = 0.90, na.rm = TRUE),
WaitTimMin_Mean = mean(WaitTime_Min2, na.rm = TRUE),
WaitTimMin_Pct10 = quantile(WaitTime_Min2, probs = 0.10, na.rm = TRUE),
WaitTimMin_Pct25 = quantile(WaitTime_Min2, probs = 0.25, na.rm = TRUE),
WaitTimMin_Pct50 = quantile(WaitTime_Min2, probs = 0.50, na.rm = TRUE),
WaitTimMin_Pct75 = quantile(WaitTime_Min2, probs = 0.75, na.rm = TRUE),
WaitTimMin_Pct90 = quantile(WaitTime_Min2, probs = 0.90, na.rm = TRUE)
) %>%
as.data.frame()
str(RouteStats)
rownames(RouteStats) <- RouteStats$Route
str(RouteStats)
View(RouteStats)
RouteStats_Scaled <- select(RouteStats,
-Route
) %>%
scale()
str(RouteStats_Scaled)
class(RouteStats_Scaled)
View(RouteStats_Scaled)
summary(RouteStats)
summary(RouteStats_Scaled)
# <- select_if(ClustData,
# function(col) is.numeric(col) |
# is.integer(col)
# ) %>%
# scale() %>%
# as.data.frame() %>%
# na.omit()
# str(ClustData_NoFact)
# summary(ClustData_NoFact)
PCA
Trnsfrm <- preProcess(select(RouteStats,
-Route
),
method = c("BoxCox", "center", "scale", "pca")
)
# loadings
Trnsfrm$rotation
RouteStats_Pca <- predict(Trnsfrm, RouteStats) %>%
select(-Route)
RouteStats_Pca
Clustering.
Are the data clusterable?
##### Are the data clusterable?
# gradient_col <- list(low = "steelblue", high = "white")
ClustData_Ends <- get_clust_tendency(RouteStats_Pca,
n = nrow(RouteStats_Pca
) - 1,
# gradient = gradient_col,
seed = 123456789
)
str(ClustData_Ends)
# Hopkins statistic
ClustData_Ends$hopkins_stat # value of 0.1657494 implies that the data are not uniformly distributed (they are "clusterable")
#plot
ClustData_Ends$plot
Clustering. How many clusters are there?
kmeans, pam, and hierarchical clustring methods, using within sum of squares and silhouette measures.
# class(RouteStats_Pca)
fviz_nbclust(RouteStats_Pca, kmeans, method = "wss") # ~8 clusters
fviz_nbclust(RouteStats_Pca, pam, method = "wss") # ~6 clusters
fviz_nbclust(RouteStats_Pca, hcut, method = "wss") # ~6 clusters
fviz_nbclust(RouteStats_Pca, kmeans, method = "silhouette") # 2 clusters
fviz_nbclust(RouteStats_Pca, pam, method = "silhouette") # 2 clusters
fviz_nbclust(RouteStats_Pca, hcut, method = "silhouette",
hc_method = "complete") # 2 clusters
Clustering. How many clusters are there?
kmeans method with the gap statistic, using bootstrap.
# Compute gap statistic
# kmeans version
set.seed(123456789)
# system.time(
gap_stat_km <- clusGap(RouteStats_Pca,
FUN = kmeans,
nstart = 25,
K.max = 10,
B = 500
)
# )
# Print
print(gap_stat_km, method = "Tibs2001SEmax")
print(gap_stat_km)
# pam version
set.seed(123456789)
gap_stat_pm <- clusGap(RouteStats_Pca,
FUN = pam,
K.max = 10,
B = 500
)
# Print
print(gap_stat_pm, method = "Tibs2001SEmax")
print(gap_stat_pm)
# hierarchical version
set.seed(123456789)
gap_stat_hcut <- clusGap(RouteStats_Pca,
FUN = hcut,
K.max = 10,
B = 500
)
# Print
print(gap_stat_hcut, method = "Tibs2001SEmax")
print(gap_stat_hcut)
# Plot kmeans
fviz_gap_stat(gap_stat_km,
maxSE = list(method = "Tibs2001SEmax")
) # 1 cluster
# Plot pam
fviz_gap_stat(gap_stat_pm,
maxSE = list(method = "Tibs2001SEmax")
) # 2 cluster
# Plot hierarchical
fviz_gap_stat(gap_stat_hcut,
maxSE = list(method = "Tibs2001SEmax")
) # 1 cluster
Clustering. How many clusters are there?
kmeans method with various different statistics.
# str(iris)
nb <- NbClust(RouteStats_Pca, #scale(iris[ ,-5]),
distance = "euclidean",
min.nc = 2,
max.nc = 15,
method = "kmeans",
index = "all"
)
fviz_nbclust(nb) + theme_minimal()
Clustering. How many clusters are there?
Hierarchical clustering method. Particularly looking at silhouette statistics.


# Visualize
HCDend_K2
HCDend_K3
HCDend_K4

HCDend_K5

HCDend_K6

HCDend_K7

HCDend_K8

HCDend_K9

HCDend_K10

HCDend_K11

HCDend_K12

HCDend_K13

HCDend_K14

HCDend_K15

HCSil_K2

HCSil_K3

HCSil_K4

HCSil_K5

HCSil_K6

HCSil_K7

HCSil_K8

HCSil_K9

HCSil_K10

HCSil_K11

HCSil_K12

HCSil_K13

HCSil_K14

HCSil_K15

HCSilWidth_AllK
Using kmeans, PAM, and Hierarchical clustering methods, we can say we probably have aroun 2 clusters.
Let’s try density clustering. (This tends to show that maybe there is only one “cluster,” meaning that data are not clusterable.)

# Compute DBSCAN using fpc package
kNNdistplot(RouteStats_Pca, k = 10)
abline(h = 8.5, lty = 2)
set.seed(123456789)
db <- fpc::dbscan(RouteStats_Pca,
eps = 8.5,
MinPts = 10
)
str(db)
List of 4
$ cluster: num [1:268] 1 1 1 1 1 1 1 1 1 1 ...
$ eps : num 8.5
$ MinPts : num 10
$ isseed : logi [1:268] TRUE TRUE TRUE TRUE TRUE TRUE ...
- attr(*, "class")= chr "dbscan"
db
dbscan Pts=268 MinPts=10 eps=8.5
0 1
border 5 7
seed 0 256
total 5 263
# Plot DBSCAN results
fviz_cluster(db,
RouteStats_Pca,
stand = FALSE,
frame = FALSE,
geom = "point"
)
argument frame is deprecated; please use ellipse instead.

Investigating TravelTime_Sec.
View(filter(TTLargeRteChng,
!is.na(TravelTime_Sec) &
RteChange2 == "Same"
) %>%
arrange(desc(TravelTime_Sec),
SpeedAvg_Mph_NewHvrs
) %>%
head(500)
)
# examples where TravelTime_Sec is small (1 sec) and SpeedAvg_Mph_NewHvrs is large.
View(select(NewTravTime,
# -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
-(TD_Mi_q2:TD_Mi_SSHG_Cnt_F),
-(TT_Hr_q2:TT_Hr_SSHG_Cnt_F)
) %>%
filter((RowNum_OG >= 2217353 & RowNum_OG <= 2217373) | # 2217363
(RowNum_OG >= 3090321 & RowNum_OG <= 3090341) | # 3090331
(RowNum_OG >= 80764 & RowNum_OG <= 80784) | # 80774
(RowNum_OG >= 33840 & RowNum_OG <= 33860) # 33850
)
)
# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(filter(TTLargeRteChng,
(RowNum_OG >= 2250290 & RowNum_OG <= 2250310) | # 2250300
(RowNum_OG >= 867717 & RowNum_OG <= 867737) | # 867727
(RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
(RowNum_OG >= 808395 & RowNum_OG <= 808415) # 808405
)
)
# examples where TravelTime_Sec is unusually small (with TravelDistance_Mi values that are large).
View(filter(AllDays_NewTravelDist,
(RowNum_OG >= 1042228 & RowNum_OG <= 1042248) | # 1042238
(RowNum_OG >= 53816 & RowNum_OG <= 53836) | # 53826
(RowNum_OG >= 360571 & RowNum_OG <= 360591) | # 360581
(RowNum_OG >= 502271 & RowNum_OG <= 502291) # 502281 (can't explian the weird TravelTime_Sec calculation here - it's not even an integer!)
)
)
# still trying to explain 502281...on the day of this weirdness, the bus was only in circulation for 4-5 stops (~20 minutes) on that day (Oct 6)
View(filter(AllDays_NewTravelDist,
Bus_ID == 2711
)
)
# exploring large values for TravelTime_Sec
View(filter(AllDays_NewTravelDist,
TravelTime_Sec == 300
) %>%
arrange(desc(TravelTime_Sec),
SpeedAvg_Mph2
)
)
# examples where TravelTime_Sec is unusually large (with TravelDistance_Mi values that are small, so SpeedAvg_Mph values are very small).
View(filter(AllDays_NewTravelDist,
(RowNum_OG >= 2627459 & RowNum_OG <= 2627479) | # 2627469
(RowNum_OG >= 2193344 & RowNum_OG <= 2193364) | # 2193354
(RowNum_OG >= 1644123 & RowNum_OG <= 1644143) | # 1644133
(RowNum_OG >= 869600 & RowNum_OG <= 869620) # 869610
)
)
Investigation of SpeedAvg_Mph2
View(Speed_Pctiles): 90% of SpeedAvg_Mph2 are between ~3mph and ~66mph.
Speed_Ntile <- as.data.frame(AllDays_NewTravelDist$SpeedAvg_Mph2) %>%
mutate(Pctile = ntile(AllDays_NewTravelDist$SpeedAvg_Mph2, 100),
MinR = min_rank(AllDays_NewTravelDist$SpeedAvg_Mph2),
PctR = percent_rank(AllDays_NewTravelDist$SpeedAvg_Mph2),
PctR_Round = round(PctR, 2)
)
colnames(Speed_Ntile)[1] <- "SpeedAvg_Mph2"
str(Speed_Ntile)
Speed_Ntile_Rows <- nrow(Speed_Ntile)
View(tail(Speed_Ntile, 500))
Speed_Pctiles <- group_by(Speed_Ntile,
PctR_Round
) %>%
summarise(
MinSpeedAtPctile = min(SpeedAvg_Mph2),
CntsAtPctile = n(),
PctsAtPctile = CntsAtPctile / Speed_Ntile_Rows
) %>%
mutate(CumSumPAtP = cumsum(PctsAtPctile)
)
View(Speed_Pctiles)
Investigation of SpeedAvg_Mph2.
Exploring the removal of outlier TravelTime_Sec and TravelDistance_Mi.
summary(select(AllDays_NewTravelDist,
SpeedAvg_Mph,
SpeedAvg_Mph2
)
)
summary(select(filter(AllDays_NewTravelDist,
TravelDistance_Mi > 0.0001893939 & # lowest non-zero percentile
TravelDistance_Mi < 1.0812500000 & # 99th percentile
TravelTime_Sec > 10.050000 & # 2nd percentile
TravelTime_Sec < 293.000000 # 98th percentile
),
SpeedAvg_Mph,
SpeedAvg_Mph2
)
)
Investigation of SpeedAvg_Mph2.
Histogram of SpeedAvg_Mph2.
Speed_HistDen <- ggplot(filter(AllDays_NewTravelDist,
!is.na(SpeedAvg_Mph2)
),
aes(x = SpeedAvg_Mph2,
y = ..density..
)
) +
geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
geom_line(stat = "density", colour = "red") +
stat_bin(binwidth = 5,
geom = "text",
size = 2.5,
vjust = 1.5,
aes(label = format(..count.., big.mark = ",")
),
) +
# geom_text(aes(label = format(..count.., big.mark = ",")
# ),
# size = 3,
# nudge_y = (..count.. * 0.1)
# ) +
coord_cartesian(xlim = c(0, 70), ylim = c(0, 0.04)
) +
# theme(legend.position="none") +
labs(title = "Variation in Travel Speed",
x = "Average Speed (mph)",
y = "Density"
)
Speed_HistDen
Investigation of SpeedAvg_Mph2.
Histogram of SpeedAvg_Mph2 after removing outlier TravelTime_Sec and TravelDistance_Mi.
View(TravDistMiNew_Pctiles)
View(TravTimeHr_Pctiles)
SpeedNoOutlier_HistDen <- ggplot(filter(AllDays_NewTravelDist,
!is.na(SpeedAvg_Mph2) &
TravelDistance_Mi_New > 0.077841005 & # 5th percentile
# TravelDistance_Mi_New < 1.0812500000 & # 99th percentile
TravelTime_Sec > 12.100000 # 4th percentile
# TravelTime_Sec < 293.000000 # 98th percentile
),
aes(x = SpeedAvg_Mph2,
y = ..density..
)
) +
geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
geom_line(stat = "density", colour = "red") +
stat_bin(binwidth = 5,
geom = "text",
size = 2.5,
vjust = 1.5,
aes(label = format(..count.., big.mark = ",")
),
) +
# geom_text(aes(label = format(..count.., big.mark = ",")
# ),
# size = 3,
# nudge_y = (..count.. * 0.1)
# ) +
coord_cartesian(xlim = c(0, 70), ylim = c(0, 0.04)
) +
# theme(legend.position="none") +
labs(title = "Variation in Travel Speed",
subtitle = "(removed low outliers of Travel Distance and Travel Time)",
x = "Average Speed (mph)",
y = "Density"
)
SpeedNoOutlier_HistDen
Investigation of SpeedAvg_Mph2.
New dataset (NoOutliers_TravelDistNTime) when removing outlier low values of TravelDistance_Mi_New and TravelTime_Sec.
View(TravDistMiNew_Pctiles)
View(TravTimeHr_Pctiles)
NoOutliers_TravelDistNTime <- filter(AllDays_NewTravelDist,
TravelDistance_Mi_New > .077841005 & # 5th percentile
# TravelDistance_Mi_New < 1.0812500000 & # 99th percentile
TravelTime_Sec > 12.100000 # 4th percentile
# TravelTime_Sec < 293.000000 # 98th percentile
)
nrow(AllDays_NewTravelDist) - nrow(NoOutliers_TravelDistNTime)
str(NoOutliers_TravelDistNTime)
summary(NoOutliers_TravelDistNTime)
Investigation of SppedAvg_Mph2.
View(Speed_NoOut_Pctiles): Aproximately 90% of SpeedAvg_Mph2 values are between ~4mph and ~56mph.
Speed_NoOut_Ntile <- as.data.frame(NoOutliers_TravelDistNTime$SpeedAvg_Mph2) %>%
mutate(Pctile = ntile(NoOutliers_TravelDistNTime$SpeedAvg_Mph2, 100),
MinR = min_rank(NoOutliers_TravelDistNTime$SpeedAvg_Mph2),
PctR = percent_rank(NoOutliers_TravelDistNTime$SpeedAvg_Mph2),
PctR_Round = round(PctR, 2)
)
colnames(Speed_NoOut_Ntile)[1] <- "SpeedAvg_Mph2"
str(Speed_NoOut_Ntile)
Speed_NoOut_Ntile_Rows <- nrow(Speed_NoOut_Ntile)
View(tail(Speed_NoOut_Ntile, 500))
Speed_NoOut_Pctiles <- group_by(Speed_NoOut_Ntile,
PctR_Round
) %>%
summarise(
MinSpeedAtPctile = min(SpeedAvg_Mph2),
CntsAtPctile = n(),
PctsAtPctile = CntsAtPctile / Speed_NoOut_Ntile_Rows
) %>%
mutate(CumSumPAtP = cumsum(PctsAtPctile)
)
View(Speed_NoOut_Pctiles)
Investigation of SppedAvg_Mph2.
Exloring odd/impossible values.
# Exploring when SpeedAvg_Mph2 is NA -- does not occur at all
nrow(filter(NoOutliers_TravelDistNTime,
is.na(SpeedAvg_Mph2)
)
)
# Exploring when SpeedAvg_Mph2 is zero -- does not occur at all
nrow(filter(NoOutliers_TravelDistNTime,
SpeedAvg_Mph2 == 0
)
)
# examples where SpeedAvg_Mph2 < 3.2848770
View(filter(AllDays_NewTravelDist,
SpeedAvg_Mph2 > 0 &
SpeedAvg_Mph2 < 3.2848770
) %>%
arrange(SpeedAvg_Mph2)
)
# examples where SpeedAvg_Mph2 < 3.2848770
View(filter(AllDays_NewTravelDist,
(RowNum_OG >= 485338 & RowNum_OG <= 485358) | # 485348 -- Extreme travel time, Route Change
(RowNum_OG >= 346952 & RowNum_OG <= 346972) | # 346962 -- Extreme travel time, Route Change
(RowNum_OG >= 70494 & RowNum_OG <= 70514) | # 70504 -- Extreme travel time, Route Change
(RowNum_OG >= 2051846 & RowNum_OG <= 2051866) # 2051856 -- Extreme travel time, Route Change
)
)
Investigation of SpeedAvg_Mph2.
Limit the dataset based on SpeedAvg_Mph2.
NoOutliersSpeed <- filter(NoOutliers_TravelDistNTime,
between(SpeedAvg_Mph2,
4.069300, # 5th percentile
56.05651 #95th percentile
)
)
nrow(NoOutliers_TravelDistNTime) - nrow(NoOutliersSpeed)
summary(NoOutliersSpeed)
TravelTime now looks like it has some odd values on the high end. So let’s look at those.
View(TravTime_NoOut_Pctiles): Virtually all trips should take less than 5 minutes. (The 99th percentile of of TravelTime is approximately 8 minutes.)
TravTime_NoOut_Ntile <- as.data.frame(NoOutliersSpeed$TravelTime_Hr) %>%
mutate(Pctile = ntile(NoOutliersSpeed$TravelTime_Hr, 100),
MinR = min_rank(NoOutliersSpeed$TravelTime_Hr),
PctR = percent_rank(NoOutliersSpeed$TravelTime_Hr),
PctR_Round = round(PctR, 2)
)
colnames(TravTime_NoOut_Ntile)[1] <- "TravelTime_Hr"
str(TravTime_NoOut_Ntile)
TravTime_NoOut_Ntile_Rows <- nrow(TravTime_NoOut_Ntile)
View(tail(TravTime_NoOut_Ntile, 500))
TravTime_NoOut_Pctiles <- group_by(TravTime_NoOut_Ntile,
PctR_Round
) %>%
summarise(
MinTravTimeHrAtPctile = min(TravelTime_Hr),
CntsAtPctile = n(),
PctsAtPctile = CntsAtPctile / TravTime_NoOut_Ntile_Rows
) %>%
mutate(CumSumPAtP = cumsum(PctsAtPctile),
MinTravTimeSecAtPctile = MinTravTimeHrAtPctile * (60 * 60)
)
View(TravTime_NoOut_Pctiles)
Investigating odd TravelTime_Sec values.
Trips longer than ~8 minutes.
View(filter(NoOutliersSpeed,
TravelTime_Sec > 491 # min at the 100th percentile
) %>%
arrange(desc(TravelTime_Sec)
)
)
# examples of TravelTime_Sec values that are largest.
View(filter(NoOutliersSpeed,
(RowNum_OG >= 2071759 & RowNum_OG <= 2071779) | # 2071769 -- results from a route change, and a 3hr+ wait before the new route starts
(RowNum_OG >= 1473686 & RowNum_OG <= 1473706) | # 1473696 -- results from a route change, and a 3hr wait before the new route starts
(RowNum_OG >= 1222822 & RowNum_OG <= 1222842) | # 1222832 -- results from a route change, and a 3hr wait before the new route starts
(RowNum_OG >= 3046089 & RowNum_OG <= 3046109) # 3046099 -- results from a route change, and a 3hr wait before the new route starts
)
)
# examples of TravelTime_Sec values that are the smallest of the large.
View(filter(NoOutliersSpeed,
(RowNum_OG >= 3044689 & RowNum_OG <= 3044709) | # 3044699 -- results from a route change
(RowNum_OG >= 3022358 & RowNum_OG <= 3022378) | # 3022368 -- results from a route change
(RowNum_OG >= 2993016 & RowNum_OG <= 2993036) | # 2993026 -- results from a previous route change (change occurred in deleted row)
(RowNum_OG >= 2683703 & RowNum_OG <= 2683723) # 2683713 -- results from a previous route change (change occurred in deleted row)
)
)
Let’s look at the TravelTime_Sec values and route changes (DirChange2).
The 99th percentile of TravelTime_Sec for both, all trips, and just those trips NOT involving route changes (DirChange2 = “Same”), is approximately 5min (300 sec).
Nota Bene: The percentile calculation here is defined slightly different than in most of the above analyses (which get the lowest value in the bin created by 100 ntiles).
summary(select(NoOutliersSpeed,
TravelTime_Sec
)
)
summary(select(filter(NoOutliersSpeed,
DirChange2 == "Same"
),
TravelTime_Sec
)
)
summary(select(filter(NoOutliersSpeed,
DirChange2 == "Change"
),
TravelTime_Sec
)
)
TravTimeSec_Qtiles_df <- data.frame(PctValue = seq(0, 100, 1),
All = seq(1, 101, 1),
Same = seq(1, 101, 1),
Change = seq(1, 101, 1)
)
TravTimeSec_Qtiles_df[ , 2] <- quantile(select(NoOutliersSpeed,
TravelTime_Sec
),
probs = seq(0, 1, 0.01),
na.rm = TRUE
)
TravTimeSec_Qtiles_df[ , 3] <- quantile(select(filter(NoOutliersSpeed,
DirChange2 == "Same"
),
TravelTime_Sec
),
probs = seq(0, 1, 0.01),
na.rm = TRUE
)
TravTimeSec_Qtiles_df[ , 4] <- quantile(select(filter(NoOutliersSpeed,
DirChange2 == "Change"
),
TravelTime_Sec
),
probs = seq(0, 1, 0.01),
na.rm = TRUE
)
View(TravTimeSec_Qtiles_df)
Limit the dataset now based on TravelTime_Sec.
UpperLimitTravTime <- filter(NoOutliersSpeed,
TravelTime_Sec <= 491 # min at the 100th percentile
)
nrow(NoOutliersSpeed) - nrow(UpperLimitTravTime)
str(UpperLimitTravTime)
summary(UpperLimitTravTime)
Investigation of Dwell_Time2 (how long the bus is at a stop).
Differences between Dwell_Time (by WMATA) and Dwell_Time2 (by me) appear to be due to switches in RouteAlt. WMATA calculates Dwell_Time by an unknown process. The WMATA calculation is equal to my calculation, except for the records immedaitely before and after a RouteAlt switch (DirChange2).
View(filter(AllDays_NewOrder,
Dwell_Time != Dwell_Time2
)
)
# Examples where the Dwell_Time and Dwell_Time2 are different
View(filter(AllDays_NewOrder,
( (RowNum_OG >= 65 & RowNum_OG <= 85) | # 75
(RowNum_OG >= 162 & RowNum_OG <= 192) | # 172
(RowNum_OG >= 431952 & RowNum_OG <= 431972) | # 431962
(RowNum_OG >= 434595 & RowNum_OG <= 434615) # 434605 -- this record is NOT a route switch, but does has a Sequence switch (Me: should there really be a route switch here?)
)
)
)
Investigation of Dwell_Time2 (how long the bus is at a stop).
First, create some “rank” stats. View(DT2_Pctiles): 95% of Dwell_Time2s are <= 23 seconds…but some weird (e.g., nearly 2 hour Dwell_Time2s exist).
DwellTime2_Ntile <- as.data.frame(AllDays_NewOrder$Dwell_Time2) %>%
mutate(Pctile = ntile(AllDays_NewOrder$Dwell_Time2, 100),
MinR = min_rank(AllDays_NewOrder$Dwell_Time2),
PctR = percent_rank(AllDays_NewOrder$Dwell_Time2),
PctR_Round = round(PctR, 2)
)
colnames(DwellTime2_Ntile)[1] <- "Dwell_Time2"
str(DwellTime2_Ntile)
DwellTime2_Ntile_Rows <- nrow(DwellTime2_Ntile)
View(tail(DwellTime2_Ntile, 500))
DwellTime2_Pctiles <- group_by(DwellTime2_Ntile,
PctR_Round
) %>%
summarise(
MinDwellAtPctile = min(Dwell_Time2),
CntsAtPctile = n(),
PctsAtPctile = CntsAtPctile / DwellTime2_Ntile_Rows
) %>%
mutate(CumSumPAtP = cumsum(PctsAtPctile)
)
View(DwellTime2_Pctiles)
Investigation of Dwell_Time2 (how long the bus is at a stop).
Histogram of Dwell_Time2.
DwellTime2_HistDen <- ggplot(AllDays_NewOrder, aes(x = Dwell_Time2, y = ..density..)) +
geom_histogram(binwidth = 1, fill = "lightblue", colour = "grey60", size = 0.2) +
geom_line(stat = "density", colour = "red") +
coord_cartesian(xlim = c(1, 25), ylim = c(0, 0.05)
) +
xlab("Time a Bus Stays at a Stop (sec)") +
ylab("Density") +
# theme(legend.position="none") +
ggtitle(expression(atop("Variation in How Long a Bus Stays at a Stop"
# ,atop(italic("xxxxx"),"")
)
)
)
DwellTime2_HistDen
Investigation of Dwell_Time2 (how long the bus is at a stop).
Looking at some weirdly long Dwell_Time2 values.
View(arrange(AllDays_NewOrder,
desc(Dwell_Time2)
)
)
# examples of extremely large Dwell_Time2s
View(filter(AllDays_NewOrder,
(RowNum_OG >= 292669 & RowNum_OG <= 292689) | # 292679
(RowNum_OG >= 531057 & RowNum_OG <= 531077) | # 531067
(RowNum_OG >= 1388627 & RowNum_OG <= 1388647) | # 1388637
(RowNum_OG >= 1645711 & RowNum_OG <= 1645731) # 1645721
)
)
View(filter(AllDays_NewOrder,
Dwell_Time2 == 0
)
)
Investigation of Delta_Time (how early or late the bus is).
View(DT2_Pctiles): 94% of Delta_Time values are between -236 seconds and 1,259 seconds. Roughly 66% of records are within 5 min late and 5 min early…but some weird (e.g., almost 50 minute late or 40 minute early) Delta_Times exist.
Note that Delta_Time is the difference from the scheduled bus arrival. So if two buses are scheduled to arrive at a destination at 10:00pm and 10:20pm, and if the 10:20pm bus has a Delta_Time of 5 minutes, there are 25 minutes between bus arrivals at the stop.
Also note that based on a comment at https://planitmetro.com/2016/11/16/data-download-metrobus-vehicle-location-data/, the Delta_Time values don’t appear to coincide with published bus schedules (e.g., the X2 departing every 8 minutes during peak hours).
DeltTime_Ntile <- as.data.frame(AllDays_NewOrder$Delta_Time) %>%
mutate(Pctile = ntile(AllDays_NewOrder$Delta_Time, 100),
MinR = min_rank(AllDays_NewOrder$Delta_Time),
PctR = percent_rank(AllDays_NewOrder$Delta_Time),
PctR_Round = round(PctR, 2)
)
colnames(DeltTime_Ntile)[1] <- "Delta_Time"
str(DeltTime_Ntile)
DeltTime_Ntile_Rows <- nrow(DeltTime_Ntile)
View(tail(DeltTime_Ntile, 500))
DeltTime_Pctiles <- group_by(DeltTime_Ntile,
PctR_Round
) %>%
summarise(
MinDeltTimeAtPctile = min(Delta_Time),
CntsAtPctile = n(),
PctsAtPctile = CntsAtPctile / DeltTime_Ntile_Rows
) %>%
mutate(CumSumPAtP = cumsum(PctsAtPctile)
)
View(DeltTime_Pctiles)
DeltTime_Pctiles
# ~66% of rows are between 5 min late and 5 min early
nrow(filter(AllDays_NewOrder,
Delta_Time >= -300 &
Delta_Time <= 300
)
) / nrow(AllDays_NewOrder)
# examples of weird large Delta_Times
View(filter(AllDays_NewOrder,
Delta_Time < -4202 |
Delta_Time > 1705
) %>%
arrange(desc(Delta_Time)
)
)
Investigation of Delta_Time (how early or late the bus is).
Delta_Time histogram.
DeltTime_HistDen <- ggplot(AllDays_NewOrder, aes(x = (Delta_Time / 60),
y = ..density..
)
) +
geom_histogram(binwidth = (5/60), fill = "lightblue", colour = "grey60", size = 0.2) +
geom_line(stat = "density", colour = "red") +
coord_cartesian(xlim = c(-5, 5)) +
xlab("Bus Lateness (min)") +
ylab("Density") +
# theme(legend.position="none") +
ggtitle(expression(atop("Variation in How Early/Late a Bus Is",
atop(italic("(positive values are late arrivals)"),
""
)
)
)
)
DeltTime_HistDen
Investigation of Delta_Time (how early or late the bus is).
Delta_Time boxplot.
# Count_Values is needed to display the medians on the box plots
Count_Values <- ddply(AllDays_NewOrder,
.(Event_Time_HrGroup),
summarise,
Value_Counts = median(Delta_Time / 60, na.rm = TRUE)
)
DeltTime_BoxPlot <- ggplot(AllDays_NewOrder,
aes(factor(Event_Time_HrGroup),
Delta_Time / 60,
fill = factor(Event_Time_HrGroup)
)
) +
geom_boxplot(outlier.colour="red", notch=TRUE) +
# coord_cartesian(ylim = c(-300, 1200)) +
coord_cartesian(ylim = c(-5, 20)) +
geom_text(data = Count_Values,
aes(y = Value_Counts,
label = format(round(Value_Counts, digits = 1),
nsmall = 1
)
),
size = 3,
vjust = -0.5
) +
xlab("Hour Group") +
ylab("Bus Lateness (minutes)") +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
#theme(legend.position="right", axis.text.x = element_blank()) +
ggtitle(expression(atop("How Early/Late is the Bus (by Hour Group)",
atop(italic("(positive values are late arrivals)"),
""
)
)
)
)
DeltTime_BoxPlot
Investigation of Delta_Time (how early or late the bus is).
Exploring “extreme” Delta_Times. First let’s get some “rank” stats.
View(DeltTime_Pctiles)
DeltTime_Pctiles
DeltTimeAbs_Ntile <- as.data.frame(abs(AllDays_NewOrder$Delta_Time)) %>%
mutate(Pctile = ntile(abs(AllDays_NewOrder$Delta_Time), 100),
MinR = min_rank(abs(AllDays_NewOrder$Delta_Time)),
PctR = percent_rank(abs(AllDays_NewOrder$Delta_Time)),
PctR_Round = round(PctR, 2)
)
colnames(DeltTimeAbs_Ntile)[1] <- "Delta_Time_Abs"
str(DeltTimeAbs_Ntile)
DeltTimeAbs_Ntile_Rows <- nrow(DeltTimeAbs_Ntile)
View(tail(DeltTimeAbs_Ntile, 500))
DeltTimeAbs_Pctiles <- group_by(DeltTimeAbs_Ntile,
PctR_Round
) %>%
summarise(
MinDeltTimeAtPctile = min(Delta_Time_Abs),
CntsAtPctile = n(),
PctsAtPctile = CntsAtPctile / DeltTime_Ntile_Rows
) %>%
mutate(CumSumPAtP = cumsum(PctsAtPctile)
)
View(DeltTimeAbs_Pctiles)
DeltTimeAbs_Pctiles
Investigation of Delta_Time (how early or late the bus is).
Exploring “extreme” Delta_Times. Then let’s calculate the percentage of buses that are 10 minutes (or more) late/early.
HrGroup_DeltaTime_All <- group_by(AllDays_NewOrder,
Event_Time_HrGroup
) %>%
summarise(EventAll_Cnt = n()
)
str(HrGroup_DeltaTime_All)
View(HrGroup_DeltaTime_All)
HrGroup_DeltaTime_Above10Min <- filter(AllDays_NewOrder,
abs(Delta_Time) >= 600
) %>%
group_by(Event_Time_HrGroup) %>%
summarise(EventAbove10_Cnt = n()
)
str(HrGroup_DeltaTime_Above10Min)
View(HrGroup_DeltaTime_Above10Min)
HrGroup_DeltaTimeCompare <- inner_join(HrGroup_DeltaTime_Above10Min,
HrGroup_DeltaTime_All,
by = c("Event_Time_HrGroup" = "Event_Time_HrGroup")
) %>%
mutate(PctEventsAbove10 = EventAbove10_Cnt / EventAll_Cnt)
View(HrGroup_DeltaTimeCompare)
Investigation of Delta_Time (how early or late the bus is).
Quickly plot these “extreme” Delta_Times.
DeltTime_Above10_Cols <- ggplot(HrGroup_DeltaTimeCompare,
aes(factor(Event_Time_HrGroup),
PctEventsAbove10
)
) +
geom_col(fill = "lightblue", colour = "grey60", size = 0.2) +
geom_text(aes(label = format(round(PctEventsAbove10, digits = 2),
nsmall = 2
)
),
size = 3,
nudge_y = (HrGroup_DeltaTimeCompare$PctEventsAbove10 * -0.1)
) +
# coord_cartesian(xlim = c(-5, 5)) +
xlab("Hour Group") +
ylab("Percent of All Bus Arrivals") +
theme(legend.position="none", axis.text.x = element_text(angle=45)) +
ggtitle(expression(atop("When is a Bus 10+ Minutes Late/Early"
# ,atop(italic("positive values are late arrivals"),
# ""
# )
)
)
)
DeltTime_Above10_Cols
Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).
Correlation.
DwellTDeltaT_Corr <- as.matrix(cor(x = AllDays_NewOrder$Dwell_Time2,
y = AllDays_NewOrder$Delta_Time,
use = "pairwise"
)
)
DwellTDeltaT_Corr
Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).
Next, let’s get a sample of data for plotting. Let’s do this for the full dataset (AllDays_NewOrder).
AllDays_NewOrder_10PctSamp <- sample_frac(AllDays_NewOrder, 0.1) %>%
select(Delta_Time,
Dwell_Time2
) %>%
mutate(DataSet = "AllData")
str(AllDays_NewOrder_10PctSamp)
Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).
Let’s also get a sample of data for plotting, but with a datset that removes outliers.
View(DeltTime_Pctiles)
View(DwellTime2_Pctiles)
AllDays_NewOrder_NoExtremes_10PctSamp <- filter(AllDays_NewOrder,
between(Delta_Time, -402, 1705) & # removes about 2% of Delta_Time values
between(Dwell_Time2, 1, 63) # removes about 2% of Dwell_Time2 values
) %>%
sample_frac(0.1) %>%
select(Delta_Time,
Dwell_Time2
) %>%
mutate(DataSet = "OutliersRemoved")
str(AllDays_NewOrder_NoExtremes_10PctSamp)
Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).
Plotting the data from the dataset that does not remove outliers.
DwellTDeltaT_Scatter <- ggplot(AllDays_NewOrder_10PctSamp,
aes(Dwell_Time2, Delta_Time)
) +
geom_point(shape = 1, alpha = 0.5) +
scale_shape(solid = FALSE) +
geom_smooth(method = "lm", colour = "red") +
# xlab("Time at Stop (sec)") +
# ylab("Lateness (sec)") +
annotate(label = lm_eqn(df = AllDays_NewOrder_10PctSamp,
y = AllDays_NewOrder_10PctSamp$Delta_Time,
x = AllDays_NewOrder_10PctSamp$Dwell_Time2
),
x = 2200,
y = 600,
geom = "text",
size = 3,
colour = "red",
parse = TRUE
) +
labs(title = "Lateness vs Time at Stop",
subtitle = "(no outliers removed)",
x = "Time at Stop (sec)",
y = "Lateness (sec)"
)
# ggtitle(expression(atop("Lateness vs Time at Stop"
# ,atop(italic("(no outliers removed)"),
# ""
# )
# )
# )
# )
# +
# geom_jitter()
DwellTDeltaT_Scatter
Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).
Plotting the data from the dataset that does remove outliers.
DwellTDeltaT_Scatter_NoExtremes <- ggplot(AllDays_NewOrder_NoExtremes_10PctSamp,
aes(Dwell_Time2, Delta_Time)
) +
geom_point(shape = 1, alpha = 0.5) +
scale_shape(solid = FALSE) +
geom_smooth(method = "lm", colour = "blue") +
# xlab("Time at Stop (sec)") +
# ylab("Lateness (sec)") +
annotate(label = lm_eqn(df = AllDays_NewOrder_NoExtremes_10PctSamp,
y = AllDays_NewOrder_NoExtremes_10PctSamp$Delta_Time,
x = AllDays_NewOrder_NoExtremes_10PctSamp$Dwell_Time2
),
x = 50,
y = -475,
geom = "text",
size = 3,
colour = "blue",
parse = TRUE
) +
labs(title = "Lateness vs Time at Stop",
subtitle = "(2% of outliers removed)",
x = "Time at Stop (sec)",
y = "Lateness (sec)"
)
# ggtitle(expression(atop("Lateness vs Time at Stop"
# ,atop(italic("(2% of outliers removed)"),
# ""
# )
# )
# )
# )
# +
# geom_jitter()
DwellTDeltaT_Scatter_NoExtremes
Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).
Plotting the data from both datasets together.
CombinedData <- rbind(AllDays_NewOrder_10PctSamp,
AllDays_NewOrder_NoExtremes_10PctSamp
)
CombinedData$DataSet <- factor(CombinedData$DataSet)
str(CombinedData)
DwellTDeltaT_Scatter_Combined <- ggplot(CombinedData,
aes(x = Dwell_Time2,
y = Delta_Time,
colour = DataSet
)
) +
geom_point(shape = 1, alpha = 0.5) +
scale_shape(solid = FALSE) +
coord_cartesian(xlim = c(0, 500), ylim = c(-1000, 2000)
) +
geom_smooth(data = filter(CombinedData,
DataSet == "AllData"
),
method = "lm",
colour = "red"
) +
geom_smooth(data = filter(CombinedData,
DataSet == "OutliersRemoved"
),
method = "lm",
colour = "blue"
) +
# facet_wrap( ~ DataSet, ncol = 2) +
annotate(label = lm_eqn(df = AllDays_NewOrder_10PctSamp,
y = AllDays_NewOrder_10PctSamp$Delta_Time,
x = AllDays_NewOrder_10PctSamp$Dwell_Time2
),
x = 300,
y = -600,
geom = "text",
size = 3,
colour = "red",
parse = TRUE
) +
annotate(label = lm_eqn(df = AllDays_NewOrder_NoExtremes_10PctSamp,
y = AllDays_NewOrder_NoExtremes_10PctSamp$Delta_Time,
x = AllDays_NewOrder_NoExtremes_10PctSamp$Dwell_Time2
),
x = 300,
y = -800,
geom = "text",
size = 3,
colour = "blue",
parse = TRUE
) +
theme(legend.position = "bottom") +
labs(title = "Lateness vs Time at Stop",
x = "Time at Stop (sec)",
y = "Lateness (sec)"
)
# ggtitle(expression(atop("Lateness vs Time at Stop"
# ,atop(italic("2% of outliers removed"),
# ""
# )
# )
# )
# )
# +
# geom_jitter()
DwellTDeltaT_Scatter_Combined
Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayBmb3IgV01BVEEgTWV0cm9idXMgRGF0YSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2sgZm9yIGFuYWx5c2lzIHVzaW5nIGRhdGEgb24gdGhlIERDIEJ1cyBTeXN0ZW0gKFdNQVRBIE1ldHJvYnVzKS4gIFRoZSBkYXRhIHdlcmUgb2J0YWluZWQgaGVyZToKCmh0dHBzOi8vcGxhbml0bWV0cm8uY29tLzIwMTYvMTEvMTYvZGF0YS1kb3dubG9hZC1tZXRyb2J1cy12ZWhpY2xlLWxvY2F0aW9uLWRhdGEvCgoKTG9hZCB0aGUgcGFja2FnZXMgdG8gYmUgdXNlZC4KYGBge3IgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CgpsaWJyYXJ5KCJqc29ubGl0ZSIpICAgICAgICAgICAjIG1hbmlwdWxhdGluZyBKU09OIGZpbGVzIGZvciB6aXAgY29kZXMKbGlicmFyeSgic3FsZGYiKSAgICAgICAgICAgICAgIyBzcWwtYmFzZWQgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeSgidGNsdGsiKQpsaWJyYXJ5KCJ0aWR5ciIpICAgICAgICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uCmxpYnJhcnkoInBseXIiKSAgICAgICAgICAgICAgICMgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeSgiZHBseXIiKSAgICAgICAgICAgICAgIyBkYXRhIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KCJtYWdyaXR0ciIpICAgICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uIChwaXBpbmcgZGF0YSkKbGlicmFyeSgic3RyaW5nciIpICAgICAgICAgICAgIyBzdHJpbmcgbWFuaXB1bGF0aW9uCmxpYnJhcnkoImRhdGEudGFibGUiKSAgICAgICAgICMgdXNlZCBpbiB0ZXN0aW5nIGRhdGEgbWFuaXB1bGF0aW9uIGZvciBzcGVlZCBpbmNyZWFzZXMKbGlicmFyeSgibHVicmlkYXRlIikgICAgICAgICAgIyBkYXRlIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KCJnZW9zcGhlcmUiKSAgICAgICAgICAjIGNhbGN1bGF0aW5nIEhhdmVyc2luZSBkaXN0YW5jZQpsaWJyYXJ5KCJnZ3Bsb3QyIikgICAgICAgICAgICAjIGdlbmVyYWwgcGxvdHRpbmcKbGlicmFyeSgiZ2d2aXMiKSAgICAgICAgICAgICAgIyBnZW5lcmFsIHBsb3R0aW5nCmxpYnJhcnkoInJib2tlaCIpICAgICAgICAgICAgICMgZ2VuZXJhbCBwbG90dGluZwpsaWJyYXJ5KCJnZ21hcCIpICAgICAgICAgICAgICAjIGdlbmVyYWwgcGxvdHRpbmcgb2YgbWFwcwpsaWJyYXJ5KCJyZ2RhbCIpICAgICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJicm9vbSIpICAgICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJtYXB0b29scyIpICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJyZ2VvcyIpICAgICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJjYXJldCIpICAgICAgICAgICAgICAjIHVzZWQgaW4gUENBCmxpYnJhcnkoImNsdXN0ZXIiKSAgICAgICAgICAgICMgdXNlZCBmb3IgY2x1c3RlcmluZwpsaWJyYXJ5KCJmcGMiKSAgICAgICAgICAgICAgICAjIHVzZWQgZm9yIGNsdXN0ZXJpbmcKbGlicmFyeSgiZGJzY2FuIikgICAgICAgICAgICAgIyB1c2VkIGZvciBjbHVzdGVyaW5nCmxpYnJhcnkoIk5iQ2x1c3QiKSAgICAgICAgICAgICMgdXNlZCBmb3IgY2x1c3RlcmluZwpsaWJyYXJ5KCJmYWN0b2V4dHJhIikgICAgICAgICAjIHBsb3R0aW5nIGNsdXN0ZXJzCiMgbGlicmFyeSgiZ3BjbGliIikKIyBpbnN0YWxsLnBhY2thZ2VzKCdyZ2VvcycsIHR5cGU9J3NvdXJjZScpCiMgaW5zdGFsbC5wYWNrYWdlcygncmdkYWwnLCB0eXBlPSdzb3VyY2UnKQojIGluc3RhbGwucGFja2FnZXMoIk5iQ2x1c3QiKQoKYGBgCgoKR2V0IHRoZSBCdXMgZGF0YS4KCkZpcnN0IGxldCdzIGNoZWNrIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4KYGBge3J9CgpnZXR3ZCgpCgpgYGAKCgpUaGVuLCBhY3R1YWxseSBnZXQgdGhlIGRhdGEuCmBgYHtyIGVjaG8gPSBGQUxTRX0KCnNldHdkKCIvVXNlcnMvbWR0dXJzZS9EZXNrdG9wL0FuYWx5dGljcy9EQ01ldHJvQnVzL0J1cyBBVkwgT2N0IDIwMTYiKQoKZm9yIChpIGluIDM6Nyl7CiAgYXNzaWduKHBhc3RlMCgiT2N0MCIsIGksICJSYXciKSwKICAgICAgICAgcmVhZC5kZWxpbShwYXN0ZTAoIjIwMTYxMDAiLCBpLCAiTWV0cm9idXNBVkwudHh0IiksCiAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSBOVUxMCiAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQogIAogIG1lc3NhZ2UoIk9jdDAiLCBpLCAiUmF3IikKIAogIHN0cihnZXQocGFzdGUwKCJPY3QwIiwgaSwgIlJhdyIpCiAgICAgICAgICkKICAgICApCiAgfQoKYGBgCgoKUHV0IHRoZSBkYWlseSBkYXRhIHRvZ2V0aGVyLgpgYGB7cn0KCkFsbERheXMgPC0gYmluZF9yb3dzKGxpc3QoT2N0MDNSYXcsIE9jdDA0UmF3LCBPY3QwNVJhdywgT2N0MDZSYXcsIE9jdDA3UmF3KSwKICAgICAgICAgICAgICAgICAgICAgLmlkID0gYygiZ3JvdXAiKQogICAgICAgICAgICAgICAgICAgICkKIyBkaW0oQWxsRGF5cykKc3RyKEFsbERheXMpCgpgYGAKCgpEZWxldGluZyBvbGQgZGF0YSBmcmFtZXMuCmBgYHtyfQoKZm9yIChpIGluIDM6Nyl7CiAgcm0obGlzdCA9IGxzKHBhdHRlcm4gPSBwYXN0ZTAoIk9jdDAiLCBpLCAiUmF3IikKICAgICAgICAgICAgICApCiAgICApCiAgCiAgbWVzc2FnZSgiRGVsZXRpbmcgT2N0MCIsIGksICJSYXciKQogIH0KCmBgYAoKClVwZGF0aW5nIHZhcmlhYmxlIHR5cGVzLgoKVGhlbiwgc29ydGluZyB0aGUgZGF0YSBhbmQgYWRkaW5nIGEgUm93TnVtYmVyICh0byBiZSB1c2VkIGZvciBpZGVudGlmeWluZyByb3dzIGxhdGVyIGluIHRoZSBhbmFseXNlcy4pCmBgYHtyfQoKcm0oaSkKCgpBbGxEYXlzJGdyb3VwIDwtIGZhY3RvcihBbGxEYXlzJGdyb3VwKQpBbGxEYXlzJFJvdXRlX0RpcmVjdGlvbiA8LSBmYWN0b3IoQWxsRGF5cyRSb3V0ZV9EaXJlY3Rpb24pCkFsbERheXMkRXZlbnRfVGltZSA8LSBhcy5QT1NJWGN0KEFsbERheXMkRXZlbnRfVGltZSwgZm9ybWF0ID0gIiVtLSVkLSV5ICVJOiVNOiVTICVwIikKQWxsRGF5cyREZXBhcnR1cmVfVGltZSA8LSBhcy5QT1NJWGN0KEFsbERheXMkRGVwYXJ0dXJlX1RpbWUsIGZvcm1hdCA9ICIlbS0lZC0leSAlSTolTTolUyAlcCIpCgpzdHIoQWxsRGF5cykKCgpBbGxEYXlzX1NvcnRlZCA8LSBhcnJhbmdlKEFsbERheXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShSb3dOdW1fT0cgPSByb3dfbnVtYmVyKCkgIyB0aGlzIGlzIHVzZWZ1bCBpbiBpZGVudGlmeSB0aGUgcm93IGxhdGVyIG9uCiAgICAgICAgKQoKcm0oQWxsRGF5cykKc3RyKEFsbERheXNfU29ydGVkKQoKIyBWaWV3KGhlYWQoQWxsRGF5c19Tb3J0ZWQsIDEwMCkpCgpgYGAKCgpJbnNwZWN0aW5nIHRoZSB2YWx1ZXMgb2YgU3RvcF9JRCwgYW5kIGZpbmRpbmcgdGhhdCBpdCBjYW4gdGFrZSB0aGUgdmFsdWVzICIiIChibGFuaykgYW5kICJOVUxMIi4KYGBge3J9CgpWaWV3KGdyb3VwX2J5KEFsbERheXNfU29ydGVkLAogICAgICAgICAgICAgIFN0b3BfSUQKICAgICAgICAgICAgICkgJT4lIAogICAgICAgc3VtbWFyaXNlKAogICAgICAgICBDbnQgPSBuKCkKICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFN0b3BfSUQpCiAgICApCgpWaWV3KGZpbHRlcihBbGxEYXlzX1NvcnRlZCwKICAgICAgICAgICAgaXMubmEoU3RvcF9JRCkgfAogICAgICAgICAgICAgIFN0b3BfSUQgPT0gIiIgfAogICAgICAgICAgICAgIFN0b3BfSUQgPT0gIk5VTEwiCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFN0b3BfRGVzYykKICAgICkKCmBgYAoKCkNyZWF0aW5nIGEgdGFibGUgb2YgZGlzdGluY3QgU3RvcF9EZXNjIHZhbHVlcyB3aGVuIFN0b3BfSUQgaXMgIiIgKGJsYW5rKSBvciAiTlVMTCIuCmBgYHtyfQoKU3RvcElEX05ldyA8LSBmaWx0ZXIoQWxsRGF5c19Tb3J0ZWQsCiAgICAgICAgICAgICAgICAgICAgIGlzLm5hKFN0b3BfSUQpIHwKICAgICAgICAgICAgICAgICAgICAgICBTdG9wX0lEID09ICIiIHwKICAgICAgICAgICAgICAgICAgICAgICBTdG9wX0lEID09ICJOVUxMIgogICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChTdG9wX0lELCBTdG9wX0Rlc2MpICU+JSAKICBkaXN0aW5jdCgpICU+JSAKICBhcnJhbmdlKFN0b3BfSUQsIFN0b3BfRGVzYykgJT4lIAogIG11dGF0ZShTdG9wSURfTmV3ID0gMTpucm93KC4pCiAgICAgICAgKQoKVmlldyhTdG9wSURfTmV3KQpTdG9wSURfTmV3CgpgYGAKCgpDcmVhdGluZyBhIGZ1bGwgdXBkYXRlZCB0YWJsZSBieSBmaWxsaW5nIGluIFN0b3BJRF9OZXcgZm9yIHdoZW4gU3RvcF9JRCBpcyAiIiAoYmxhbmspIG9yIE5VTEwuCmBgYHtyfQoKQWxsRGF5c19TdG9wSUROZXcgPC0gbGVmdF9qb2luKEFsbERheXNfU29ydGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFN0b3BJRF9OZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9EZXNjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9OZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlN0b3BfRGVzYyIgPSAiU3RvcF9EZXNjIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFN0b3BJRF9DbGVhbiA9IGlmZWxzZShpcy5uYShTdG9wSURfTmV3KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wSURfTmV3CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFN0b3BJRF9JbmRpY2F0b3IgPSBmYWN0b3IoaWZlbHNlKGlzLm5hKFN0b3BJRF9OZXcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSURfT0siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSURfQmFkIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCgpybShTdG9wSURfTmV3KQpybShBbGxEYXlzX1NvcnRlZCkKc3RyKEFsbERheXNfU3RvcElETmV3KQoKIyBWaWV3KHRhaWwoQWxsRGF5c19TdG9wSUROZXcsIDUwMCkpCiMgVmlldyhmaWx0ZXIoQWxsRGF5c19TdG9wSUROZXcsCiMgICAgICAgICAgICAgU3RvcF9EZXNjID09ICJNRVRST1dBWSBBTk5OT1VDRU1OVCBDT1JSIgojICAgICAgICAgICAgKQojICAgICApCgpgYGAKCgpMYXQgTG9uZyBzdGF0cyBmb3IgcHVsbGluZyBpbiBaaXAgY29kZXMgbGF0ZXIuCmBgYHtyfQoKTExfU3RhdHMgPC0gZ3JvdXBfYnkoQWxsRGF5c19TdG9wSUROZXcsCiAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9DbGVhbgogICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShMYXRfTWVhbiA9IG1lYW4oTGF0aXR1ZGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIExhdF9NZWQgPSBtZWRpYW4oTGF0aXR1ZGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIExuZ19NZWFuID0gbWVhbihMb25naXR1ZGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIExuZ19NZWQgPSBtZWRpYW4oTG9uZ2l0dWRlLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKExhdF9NZWFMZXNzTWVkID0gTGF0X01lYW4gLSBMYXRfTWVkLAogICAgICAgICBMbmdfTWVhTGVzc01lZCA9IExuZ19NZWFuIC0gTG5nX01lZCwKICAgICAgICAgUm93TnVtID0gcm93X251bWJlcigpCiAgICAgICAgKQoKc3RyKExMX1N0YXRzKQpzdW1tYXJ5KExMX1N0YXRzKQoKVmlldyhoZWFkKGFycmFuZ2UoTExfU3RhdHMsCiAgICAgICAgICAgICAgICAgIExhdF9NZWFMZXNzTWVkCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpWaWV3KGhlYWQoYXJyYW5nZShMTF9TdGF0cywKICAgICAgICAgICAgICAgICAgZGVzYyhMYXRfTWVhTGVzc01lZCkKICAgICAgICAgICAgICAgICApLAogICAgICAgICAgNTAwCiAgICAgICAgICkKICAgICkKClZpZXcoaGVhZChhcnJhbmdlKExMX1N0YXRzLAogICAgICAgICAgICAgICAgICBMbmdfTWVhTGVzc01lZAogICAgICAgICAgICAgICAgICksCiAgICAgICAgICA1MDAKICAgICAgICAgKQogICAgKQoKVmlldyhoZWFkKGFycmFuZ2UoTExfU3RhdHMsCiAgICAgICAgICAgICAgICAgIGRlc2MoTG5nX01lYUxlc3NNZWQpCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpgYGAKCgpQdWxsaW5nIGluIFppcCBDb2RlIGRhdGEgZnJvbSBhcGkuZ2VvbmFtZXMub3JnLgpgYGB7cn0KCiMgVVJMIEVYQU1QTEU6CiMgaHR0cDovL2FwaS5nZW9uYW1lcy5vcmcvZmluZE5lYXJieVBvc3RhbENvZGVzSlNPTj9sYXQ9MzguODk1NjAmbG5nPS03Ni45NDg3MyZyYWRpdXM9MCZ1c2VybmFtZT1zdXBlcm1kYXQKCnVybF8xIDwtICJodHRwOi8vYXBpLmdlb25hbWVzLm9yZy9maW5kTmVhcmJ5UG9zdGFsQ29kZXNKU09OP2xhdD0iCnVybF8yIDwtICImbG5nPSIKdXJsXzMgPC0gIiZyYWRpdXM9MCZ1c2VybmFtZT0iCnVzZXJuYW1lIDwtICJzdXBlcm1kYXQiCgoKIyBuZWVkIHRvIGdyb3VwIGluIGJ1bmNoZXMgYXMgaHR0cDovL2FwaS5nZW9uYW1lcy5vcmcgbGltaXRzIHB1bGxzIHRvIDIwMDAgcGVyIGhvdXIKCgojIyMjIyBTdG9yZSBldmVyeXRoaW5nIGluIG11bHRpcGxlIGxpc3RzCnBhZ2VzMSA8LSBsaXN0KCkKCgpzeXN0ZW0udGltZSgKCmZvcihpIGluIDE6MTAwMCl7CiAgbGF0IDwtIGZpbHRlcihMTF9TdGF0cywKICAgICAgICAgICAgICAgIFJvd051bSA9PSBpCiAgICAgICAgICAgICAgICkgJT4lCiAgICBzZWxlY3QoTGF0X01lZCkKICAKICBsbmcgPC0gZmlsdGVyKExMX1N0YXRzLAogICAgICAgICAgICAgICAgUm93TnVtID09IGkKICAgICAgICAgICAgICAgKSAlPiUKICAgIHNlbGVjdChMbmdfTWVkKQogIAogIEFQSURhdGExIDwtIGZyb21KU09OKHBhc3RlMCh1cmxfMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlcm5hbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgIGZsYXR0ZW4gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICApCiAgCiAgbWVzc2FnZSgiUmV0cmlldmluZyBaaXAgQ29kZSAiLCBpKQogIAogIHBhZ2VzMVtbaV1dIDwtIEFQSURhdGExJHBvc3RhbENvZGVzCiAgCn0KKQoKCiMjIyMjIENvbWJpbmUgdGhlIGxpc3RzIGludG8gb25lIHBhZ2UKWmlwczEgPC0gcmJpbmQucGFnZXMocGFnZXMxW3NhcHBseShwYWdlczEsIGxlbmd0aCkgPiAwXSkKCgojIyMjIyBDb21iaW5lIGFsbCBwYWdlcwpaaXBzX0FsbCA8LSBiaW5kX3Jvd3MoWmlwczAsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzMSwKICAgICAgICAgICAgICAgICAgICAgIFppcHMyLAogICAgICAgICAgICAgICAgICAgICAgWmlwczMsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzNCwKICAgICAgICAgICAgICAgICAgICAgIFppcHM1LAogICAgICAgICAgICAgICAgICAgICAgWmlwczYsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzNywKICAgICAgICAgICAgICAgICAgICAgIFppcHM4LAogICAgICAgICAgICAgICAgICAgICAgWmlwczksCiAgICAgICAgICAgICAgICAgICAgICBaaXBzMTAsCiAgICAgICAgICAgICAgICAgICAgICAuaWQgPSAiaWQiCiAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShVbmlxdWVMYXRMbmcgPSBwYXN0ZShsYXQsIGxuZywgc2VwID0gIl9fIikKICAgICAgICApCgojIHN0cihaaXBzX0FsbCkKIyBWaWV3KGhlYWQoWmlwc19BbGwpKQoKCiMgc3RyKExMX1N0YXRzKQpMTF9TdGF0c19VbnFMYXRMbmcgPC0gbXV0YXRlKExMX1N0YXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFVuaXF1ZUxhdExuZyA9IHBhc3RlKExhdF9NZWQsIExuZ19NZWQsIHNlcCA9ICJfXyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgojIHN0cihMTF9TdGF0c19VbnFMYXRMbmcpCiMgVmlldyhoZWFkKExMX1N0YXRzX1VucUxhdExuZykpCgoKTExfU3RhdHNaaXBzIDwtIGxlZnRfam9pbihMTF9TdGF0c19VbnFMYXRMbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgWmlwc19BbGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJVbmlxdWVMYXRMbmciID0gIlVuaXF1ZUxhdExuZyIpCiAgICAgICAgICAgICAgICAgICAgICAgICApCgpzdHIoTExfU3RhdHNaaXBzKQojIFZpZXcoaGVhZChMTF9TdGF0c1ppcHMpKQoKIyBOb3Qgc3VyZSB3aGV5IHRoZXNlIGNvdWxkbid0IGJlIGZvdW5kICh3aHkgdGhleSdyZSBOQSkKVmlldyhmaWx0ZXIoTExfU3RhdHNaaXBzLAogICAgICAgICAgICBpcy5uYShwb3N0YWxDb2RlKQogICAgICAgICAgICkKICAgICkKCmBgYAoKCkpvaW4gdG8gY3JlYXRlIG9uZSBkYXRhc2V0IHRoYXQgYWxzbyBpbmNsdWRlcyBaaXAgdmFyaWFibGVzLgpgYGB7cn0KCnJtKHVybF8xLCB1cmxfMiwgdXJsXzMsIHVzZXJuYW1lLCBwYWdlczAsIHBhZ2VzMSwgcGFnZXMyLCBwYWdlczMsIHBhZ2VzNCwgcGFnZXM1LCBwYWdlczYsIHBhZ2VzNywgcGFnZXM4LCBwYWdlczksIHBhZ2VzMTAsIGksIGxhdCwgbG5nLCBBUElEYXRhMCwgQVBJRGF0YTEsIEFQSURhdGEyLCBBUElEYXRhMywgQVBJRGF0YTQsIEFQSURhdGE1LCBBUElEYXRhNiwgQVBJRGF0YTcsIEFQSURhdGE4LCBBUElEYXRhOSwgQVBJRGF0YTEwLCBMTF9TdGF0cywgTExfU3RhdHNfVW5xTGF0TG5nKQoKCkFsbERheXNfWmlwcyA8LSBsZWZ0X2pvaW4oQWxsRGF5c19TdG9wSUROZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgTExfU3RhdHNaaXBzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiU3RvcElEX0NsZWFuIiA9ICJTdG9wSURfQ2xlYW4iKQogICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgcmVuYW1lKFN0b3BfU3RhdGUgPSBhZG1pbkNvZGUxLAogICAgICAgICBTdG9wX0NvdW50eSA9IGFkbWluTmFtZTIsCiAgICAgICAgIFN0b3BfQ2l0eSA9IHBsYWNlTmFtZSwKICAgICAgICAgU3RvcF9aaXAgPSBwb3N0YWxDb2RlCiAgICAgICAgICkKCnJtKEFsbERheXNfU3RvcElETmV3LCBMTF9TdGF0c1ppcHMpCnN0cihBbGxEYXlzX1ppcHMpCgpgYGAKCgpVcGRhdGluZyB2YXJpYWJsZSB0eXBlcy4KYGBge3J9CgpBbGxEYXlzX1ppcHMkU3RvcF9TdGF0ZSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJFN0b3BfU3RhdGUpCkFsbERheXNfWmlwcyRTdG9wX0NvdW50eSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJFN0b3BfQ291bnR5KQpBbGxEYXlzX1ppcHMkU3RvcF9aaXAgPC0gZmFjdG9yKEFsbERheXNfWmlwcyRTdG9wX1ppcCkKQWxsRGF5c19aaXBzJFN0b3BfQ2l0eSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJFN0b3BfQ2l0eSkKCkFsbERheXNfWmlwcyRkaXN0YW5jZSA8LSBhcy5udW1lcmljKEFsbERheXNfWmlwcyRkaXN0YW5jZSkKQWxsRGF5c19aaXBzJGNvdW50cnlDb2RlIDwtIGZhY3RvcihBbGxEYXlzX1ppcHMkY291bnRyeUNvZGUpCkFsbERheXNfWmlwcyRhZG1pbk5hbWUxIDwtIGZhY3RvcihBbGxEYXlzX1ppcHMkYWRtaW5OYW1lMSkKCnN0cihBbGxEYXlzX1ppcHMpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKSW5zcGVjdGluZyBpbmNpZGVuY2VzIG9mIGNvbnNlY3V0aXZlIFN0b3BfSURzLiBUaGlzIGlzIGRvbmUgYmVjYXVzZSBpbnZlc3RpZ2F0aW9uIHNob3dlZCB0aGF0IG1hbnkgY29uc2V1dGl2ZSBldmVudHMgb2NjdXJyIGF0IHRoZSBzYW1lIFN0b3BfSUQsIGJ1dCB3aXRoIHZhcmlvdXMgRHdlbGxfVGltZXMsIE9kb21ldGVyX0Rpc3RhbmNlcywgZXRjLiAgQWxsIG9mIHdoaWNoIGFmZmVjdCBjYWxjdWxhdGlvbnMgYW5kIGFuYWx5c2VzLgoKQ3JlYXRlIGRhdGEgb24gdGhlIHJ1bnMgKGNvbnNlY3V0aXZlIFN0b3BfSURzKS4KYGBge3J9CgpTdG9wSURfUnVucyA8LSBybGUoQWxsRGF5c19aaXBzJFN0b3BJRF9DbGVhbikKClN0b3BJRF9SdW5zJGVuZHMgPC0gY3Vtc3VtKFN0b3BJRF9SdW5zJGxlbmd0aHMpCgpTdG9wSURfUnVucyRzdGFydHMgPC0gaWZlbHNlKGlzLm5hKGxhZyhTdG9wSURfUnVucyRlbmRzKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhZyhTdG9wSURfUnVucyRlbmRzKSArIDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnN0cihTdG9wSURfUnVucykKIyBjbGFzcyhTdG9wSURfUnVucykKIyAKIyBTdG9wSURfUnVuc19kZiA8LSBkYXRhLmZyYW1lKHVuY2xhc3MoU3RvcElEX1J1bnMpKQojIHN0cihTdG9wSURfUnVuc19kZikKIyBjbGFzcyhTdG9wSURfUnVuc19kZikKIyBybShTdG9wSURfUnVuc19kZikKCmBgYAoKClRyeWluZyB0byBsaW5rIGRhdGEgb24gUnVuc0dyb3VwcyB3aXRoIHRoZSBvcmlnaW5hbCBkYXRhIChBbGxEYXlzX1NvcnRlZCkuIFRoZSBnb2FsIGlzIHRvIHNlbGVjdCBvbmx5IG9uZSByZWNvcmQgcGVyIFJ1bnNHcm91cCAtIHRoYXQgYmVpbmcgdGhlIHJlY29yZCB3aXRoIHRoZSBsb25nZXN0IER3ZWxsX1RpbWUuCgpJIGF0dGVtcHRlZCB0aGlzIGNvbXB1dGF0aW9uIHVzaW5nIGJvdGggZGF0YS5mcmFtZXMgKGRwbHlyKSBhbmQgZGF0YS50YWJsZXMgKGRhdGEudGFibGUpLiBIb3dldmVyLCB3aXRoIDIsODA5LDA2MiByb3dzIGluIG9uZSBkYXRhc2V0IGFuZCAzLDExOSw0NDMgcm93cyBpbiB0aGUgb3RoZXIgZGF0YXNldCwgdGhlIGN1cnJlbnQgY29tcHV0YXRpb24gdGltZSBpcyBvdmVyIDUgZGF5cy4uLnNvIEknbSB0cnlpbmcgYSBkaWZmZXJlbnQgc3RyYXRlZ3kgdG8gb25seSBzZWxlY3QgdGhlIGZpcnN0IHJlY29yZCBpbiBhIHJ1bi4KYGBge3J9CgojIENyZWF0ZSBhIFJ1bnNHcm91cCB2YXJpYWJsZSBmb3IgZWFjaCBydW4KIyBTdG9wSURfUnVuc19kZiRSdW5zR3JvdXAgPC0gcGFzdGUwKCJnIiwgc2VxKDE6bnJvdyhTdG9wSURfUnVuc19kZikKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgCiMgc3RyKFN0b3BJRF9SdW5zX2RmKQojIGhlYWQoU3RvcElEX1J1bnNfZGYsIDI1KQojIHRhaWwoU3RvcElEX1J1bnNfZGYsIDI1KQojIAojIFN0b3BJRF9SdW5zX2RmIDwtIFN0b3BJRF9SdW5zX2RmICU+JSAKIyAgIG11dGF0ZShSb3dOdW0gPSByb3dfbnVtYmVyKCkKIyAgICAgICAgICkKIyAKIyBzdHIoU3RvcElEX1J1bnNfZGYpCiMgaGVhZChTdG9wSURfUnVuc19kZiwgMjUpCiMgdGFpbChTdG9wSURfUnVuc19kZiwgMjUpCiMgCiMgCiMgIyBDb252ZXJ0aW5nIHRvIGRhdGEudGFibGVzIGZvciwgaG9wZWZ1bGx5LCBpbXByb3ZlZCBwZXJmb3JtYW5jZSAoc3BlZWQpIGluIGNvbXB1dGF0aW9uCiMgU3RvcElEX1J1bnNfZHQgPC0gZGF0YS50YWJsZShTdG9wSURfUnVuc19kZikKIyBzZXRrZXkoU3RvcElEX1J1bnNfZHQsIFJvd051bSkKIyBzdHIoU3RvcElEX1J1bnNfZHQpCiMgCiMgQWxsRGF5c19Tb3J0ZWRfZHQgPC0gZGF0YS50YWJsZShBbGxEYXlzX1NvcnRlZCkKIyBzZXRrZXkoQWxsRGF5c19Tb3J0ZWRfZHQsIFJvd051bV9PRykKIyBzdHIoQWxsRGF5c19Tb3J0ZWRfZHQpCiMgIyBybShBbGxEYXlzX1NvcnRlZF9kdCkKIyAKIyAKIyAjIEFjdHVhbCBsb29wIHRvIHBlcmZvcm0gdGhlIGNvbXB1dGF0aW9ucyBhbmQgbGluayB0byBvcmlnaW5hbCBkYXRhIChBbGxEYXlzX1NvcnRlZF9kdCkKIyBHcm91cERhdGEgPC0gbGlzdCgpCiMgZm9yKGkgaW4gMTpucm93KFN0b3BJRF9SdW5zX2R0KQojICAgICkgewojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSksCiMgICAgICAgICAgICBTdG9wSURfUnVuc19kdFtSb3dOdW0gPT0gaSwgUnVuc0dyb3VwXQojICAgICAgICAgICApCiMgCiMgICAgICMjIyMjICBUaGUgY29kZSBiZWxvdyBpcyB0aGUgc2FtZSBjb2RlIGFzIGFib3ZlLCBidXQgZG9uZSB3aXRoIGRwbHlyICAjIyMjIwojIAojICAgICAjIGFzc2lnbihwYXN0ZTAoImdyb3VwXyIsIGkpLAojICAgIyAgICAgICAgZmlsdGVyKFN0b3BJRF9SdW5zX2RmLAojICAgIyAgICAgICAgICAgICAgIFJvd051bSA9PSBpCiMgICAjICAgICAgICAgICAgICApICU+JSAKIyAgICMgICAgICAgICAgc2VsZWN0KFJ1bnNHcm91cCkKIyAgICMgICAgICAgKQojIAojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9zdGFydCIpLAojICAgICAgICAgIFN0b3BJRF9SdW5zX2R0W1Jvd051bSA9PSBpLCBzdGFydHNdCiMgICAgICAgICApCiMgCiMgICBhc3NpZ24ocGFzdGUwKCJncm91cF8iLCBpLCAiX2VuZCIpLAojICAgICAgICAgIFN0b3BJRF9SdW5zX2R0W1Jvd051bSA9PSBpLCBlbmRzXQojICAgICAgICAgKQojIAojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9yb3dzIiksCiMgICAgICAgICAgQWxsRGF5c19Tb3J0ZWRfZHRbUm93TnVtX09HID49IGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9zdGFydCIpCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HIDw9IGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9lbmQiKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuc0dyb3VwIDo9IGFzLmNoYXJhY3RlcihnZXQocGFzdGUwKCJncm91cF8iLCBpKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBdCiMgCiMgICAgICMjIyMjICBUaGUgY29kZSBiZWxvdyBpcyB0aGUgc2FtZSBhcyB0aGUgY29kZSBhYm92ZSwgYnV0IGRvbmUgd2l0aCBkcGx5ciAgIyMjIyMKIyAKIyAgICAgICAgICAjIGZpbHRlcihBbGxEYXlzX1NvcnRlZCwKIyAgICAgICAgICAjICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywKIyAgICAgICAgICAjICAgICAgICAgICAgICAgIGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9zdGFydCIpCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgYXMubnVtZXJpYyhnZXQocGFzdGUwKCJncm91cF8iLCBpLCAiX2VuZCIpCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICkgJT4lIAojICAgICAgICAgICMgICBtdXRhdGUoUnVuc0dyb3VwID0gYXMuY2hhcmFjdGVyKGdldChwYXN0ZTAoImdyb3VwXyIsIGkpCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICApCiMgICAgICAgICApCiMgCiMgICBHcm91cERhdGFbW2ldXSA8LSBnZXQocGFzdGUwKCJncm91cF8iLCBpLCAiX3Jvd3MiKSkKIyAKIyAgIG1lc3NhZ2UoIlByb2Nlc3NpbmcgR3JvdXAgIiwgaSwgIiBvZiAyLDgwOSwwNjIiKQojIH0KIyAKIyAKIyBHcm91cERhdGFfZGYgPC0gcmJpbmQuZmlsbChHcm91cERhdGEpCiMgc3RyKEdyb3VwRGF0YV9kZikKIyBoZWFkKEdyb3VwRGF0YV9kZikKIyB0YWlsKEdyb3VwRGF0YV9kZikKIyAjIHJtKEdyb3VwRGF0YV9kZikKIyAKIyAKIyBncm91cF8xCiMgZ3JvdXBfMV9zdGFydAojIGdyb3VwXzFfZW5kCiMgZ3JvdXBfMV9yb3dzCiMgZ3JvdXBfMl9yb3dzCiMgZ3JvdXBfM19yb3dzCiMgZ3JvdXBfNTBfcm93cwojIHN0cihncm91cF81MF9yb3dzKQojIGdyb3VwXzI4MDkwNjJfcm93cwojIEdyb3VwRGF0YVtbMV1dCiMgR3JvdXBEYXRhW1s1MF1dCiMgCiMgCiMgIyMjIyMgIFRlc3RpbmcgQXJlYSAoQmVsb3cpICAjIyMjIwojICMjIyMjICBUZXN0aW5nIEFyZWEgKEJlbG93KSAgIyMjIyMKIyAjIyMjIyAgVGVzdGluZyBBcmVhIChCZWxvdykgICMjIyMjCiMgCiMgIyBoZWFkKFN0b3BJRF9SdW5zJHN0YXJ0cywgMjApCiMgIyBoZWFkKEFsbERheXNfTmV3T3JkZXIkU3RvcF9JRCwgMjApCiMgIyAKIyAjIAojICMgZGF0IDwtIGFzLmRhdGEuZnJhbWUoYygxLDEsNyw3LDcsOSw2LDgsMiwyLDIsMSwxLDEsMSwxKSkKIyAjIGNvbG5hbWVzKGRhdClbMV0gPC0gImRhdCIKIyAjIHIgPC0gcmxlKGRhdCRkYXQpCiMgIyBkYXQkcnVuIDwtIHJlcChyJGxlbmd0aHMsIHIkbGVuZ3RocykKIyAjIGRhdCRydW5MYWcgPC0gbGFnKGRhdCRydW4pCiMgIyBkYXQkY29uZCA8LSByZXAociR2YWx1ZXMsIHIkbGVuZ3RocykKIyAjIGRhdAojICMgVmlldyhkYXQpCgpgYGAKCgpXaGVuIGNvbnNlY3V0aXZlIFN0b3BfSUQgb2NjdXJycywgb25seSB0YWtlIHRoZSBmaXJzdCBvY2N1cnJlbmNlLiBUaGlzIGlzIGRvbmUgYmVjYXVzZSB0aGUgY29tcHV0YXRpb24gdGltZSB0byBzZWxlY3Qgb25seSB0aGUgcmVjb3JkIHdpdGggdGhlIGxvbmdlc3QgRHdlbGxfVGltZSBmb3IgZWFjaCBydW4gd2FzIHRvbyBsb25nIChvdmVyIDUgZGF5cykuCgpUaGlzIGlzIHByb2JhYmx5IGxlc3MgdGhhbiBpZGVhbCB3aXRoIHJlZ2FyZHMgdG8gRHdlbGxfVGltZSwgYnV0IHNob3VsZCBub3QgbWFrZSBtdWNoIGRpZmZlcmVuY2UgZm9yIGNhbGN1bGF0aW9ucyBvZiB0cmF2ZWwgdGltZSwgc3BlZWQsIGV0Yy4KYGBge3J9CgpBbGxEYXlzX0ZpcnN0U3RvcElEIDwtIEFsbERheXNfWmlwc1tTdG9wSURfUnVucyRzdGFydHMsIF0KCmRpbShBbGxEYXlzX1ppcHMpCmRpbShBbGxEYXlzX0ZpcnN0U3RvcElEKQoKbnJvdyhBbGxEYXlzX1ppcHMpIC0gbnJvdyhBbGxEYXlzX0ZpcnN0U3RvcElEKQoKcm0oQWxsRGF5c19aaXBzLCBTdG9wSURfUnVucykKc3RyKEFsbERheXNfRmlyc3RTdG9wSUQpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ3JlYXRpbmcgbmV3IHZhcmlhYmxlcy4KYGBge3J9CgpBbGxEYXlzX0FkZFZhcnMgPC0gbXV0YXRlKEFsbERheXNfRmlyc3RTdG9wSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2VfTWkgPSBPZG9tZXRlcl9EaXN0YW5jZSAvIDUyODAsICM1LDI4MCBmZWV0IGluIDEgbWlsZQogICAgICAgICAgICAgICAgICAgICAgICAgIER3ZWxsX1RpbWUyID0gYXMubnVtZXJpYyhEZXBhcnR1cmVfVGltZSAtIEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfWXIgPSBhcy5pbnRlZ2VyKHllYXIoRXZlbnRfVGltZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfTXRoID0gYXMuaW50ZWdlcihtb250aChFdmVudF9UaW1lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlID0gZGF5KEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5ID0gd2RheShFdmVudF9UaW1lLCBsYWJlbCA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIgPSBob3VyKEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfTWluID0gbWludXRlKEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cCA9IGZhY3RvcihpZmVsc2UoRXZlbnRfVGltZV9IciA8IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAwXzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAzXzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXA2XzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDEyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwOV8xMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxMl8xNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMTgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxNV8xNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxOF8yMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMjQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAyMV8yMyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSkpKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkdyb3VwMF8yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAzXzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDZfOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwOV8xMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMTJfMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDE1XzE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxOF8yMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMjFfMjMiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICkKCnJtKEFsbERheXNfRmlyc3RTdG9wSUQpCnN0cihBbGxEYXlzX0FkZFZhcnMpCgpgYGAKCgpGdW5jdGlvbiBmb3IgY2FsY3VsYXRpbmcgdGhlIGRpc3RhbmNlIHRyYXZlbGVkIGJhc2VkIG9uIHRoZSBIYXZlcnNpbmUgZm9ybXVsYS4gIE9yaWdpbmFsIGNvZGUgZnJvbTogaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vZ3JlYXQtY2lyY2xlLWRpc3RhbmNlLWNhbGN1bGF0aW9ucy1pbi1yLwpgYGB7cn0KCiMgQ2FsY3VsYXRlcyB0aGUgZ2VvZGVzaWMgZGlzdGFuY2UgYmV0d2VlbiB0d28gcG9pbnRzIHNwZWNpZmllZCBieSByYWRpYW4gbGF0aXR1ZGUvbG9uZ2l0dWRlIHVzaW5nIHRoZSBIYXZlcnNpbmUgZm9ybXVsYSAoaGYpCiMgZ2NkLmhmIDwtIGZ1bmN0aW9uKGxvbmcxLCBsYXQxLCBsb25nMiwgbGF0MikgewojICAgUiA8LSA2MzcxICMgRWFydGggbWVhbiByYWRpdXMgW2ttXQojICAgZGVsdGEubG9uZyA8LSAobG9uZzIgLSBsb25nMSkKIyAgIGRlbHRhLmxhdCA8LSAobGF0MiAtIGxhdDEpCiMgICBhIDwtIHNpbihkZWx0YS5sYXQvMileMiArIGNvcyhsYXQxKSAqIGNvcyhsYXQyKSAqIHNpbihkZWx0YS5sb25nLzIpXjIKIyAgIGMgPC0gMiAqIGFzaW4obWluKDEsc3FydChhKSkpCiMgICBkID0gUiAqIGMgKiAwLjYyMTM3MSAjIDEga20gPSAwLjYyMTM3MSBtaWxlcwojICAgcmV0dXJuKGQpICMgRGlzdGFuY2UgaW4gbWlsZXMKIyB9CgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ3JlYXRpbmcgbW9yZSB2YXJpYWJsZXMuIENyZWF0aW5nIGEgQnVzRXZlbnQgcm93IG51bWJlciBmb3IgZnV0dXJlIGlkZW50aWZpY2F0aW9uIHB1cnBvc2VzLiBUaGVuLCBjcmVhdGluZyB2YXJpb3VzIHZhcmlhYmxlcyB0byBhbmFseXplIGRpc3RhbmNlIHRyYXZlbGVkIGFuZCBzcGVlZC4KYGBge3J9CgpBbGxEYXlzX0J1c0RheSA8LSBncm91cF9ieShBbGxEYXlzX0FkZFZhcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEJ1c19JRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKEJ1c0RheV9FdmVudE51bSA9IHJvd19udW1iZXIoKSwgICMgdXNlZCB0byBpZGVudGlmeSBCdXMgbW92ZW1lbnRzIG9uIGEgcGFydGljdWxhciBkYXRlCiAgICAgICAgIAogICAgICAgICBSb3V0ZV9MYWcxID0gbGFnKFJvdXRlKSwgICMgdXNlZCBpbiBmdXR1cmUgYW5hbHlzZXMgdG8gaWRlbnRpZnkgUm91dGUgY2hhbmdlcwogICAgICAgICBSb3V0ZUFsdF9MYWcxID0gbGFnKFJvdXRlQWx0KSwgICMgdXNlZCBpbiBmdXR1cmUgYW5hbHlzZXMgdG8gaWRlbnRpZnkgUm91dGVBbHQgKGRpcmVjdGlvbikgY2hhbmdlcwogICAgICAgICAKICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2VfTGFnMSA9IGxhZyhPZG9tZXRlcl9EaXN0YW5jZSksCiAgICAgICAgIAogICAgICAgICBMYXRpdHVkZV9MMSA9IGxhZyhMYXRpdHVkZSksCiAgICAgICAgIExvbmdpdHVkZV9MMSA9IGxhZyhMb25naXR1ZGUpLAogICAgICAgICAjIExhdF9SYWRpYW4gPSBMYXRpdHVkZSpwaS8xODAsCiAgICAgICAgICMgTG9uZ19SYWRpYW4gPSBMb25naXR1ZGUqcGkvMTgwLAogICAgICAgICAjIExhdF9SYWRpYW5fTDEgPSBsYWcoTGF0X1JhZGlhbiksCiAgICAgICAgICMgTG9uZ19SYWRpYW5fTDEgPSBsYWcoTG9uZ19SYWRpYW4pLAogICAgICAgICAKICAgICAgICAgIyBhY2NvdW50aW5nIGZvciBwb3RlbnRpYWwgbmVnYXRpdmUgZGlzdGFuY2VzCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX0Z0ID0gaWZlbHNlKE9kb21ldGVyX0Rpc3RhbmNlID4gT2RvbWV0ZXJfRGlzdGFuY2VfTGFnMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2UgLSBPZG9tZXRlcl9EaXN0YW5jZV9MYWcxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID0gVHJhdmVsRGlzdGFuY2VfRnQgLyA1MjgwLCAjNSwyODAgZmVldCBpbiAxIG1pbGUKICAgICAgICAgCiAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWkyID0gZ2NkLmhmKGxvbmcxID0gTG9uZ19SYWRpYW5fTDEsCiAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdDEgPSBMYXRfUmFkaWFuX0wxLAogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb25nMiA9IExvbmdfUmFkaWFuLAogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXQyID0gTGF0X1JhZGlhbgogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIAogICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaWZlbHNlKChpcy5uYShMb25naXR1ZGVfTDEpIHwgaXMubmEoTGF0aXR1ZGVfTDEpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgIE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0SGF2ZXJzaW5lKGNiaW5kKExvbmdpdHVkZV9MMSwgTGF0aXR1ZGVfTDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNiaW5kKExvbmdpdHVkZSwgTGF0aXR1ZGUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICogMC4wMDA2MjEzNzEsICMgMC4wMDA2MjEzNzEgbWlsZXMgPSAxIG1ldGVyCiAgICAgICAgIAogICAgICAgICAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSB0aW1lcwogICAgICAgICBUcmF2ZWxUaW1lX1NlYyA9IGFzLm51bWVyaWMoaWZlbHNlKEV2ZW50X1RpbWUgPiBsYWcoRGVwYXJ0dXJlX1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUgLSBsYWcoRGVwYXJ0dXJlX1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRyYXZlbFRpbWVfSHIgPSBUcmF2ZWxUaW1lX1NlYyAvIDM2MDAsICMgMyw2MDAgc2Vjb25kcyBpbiAxIGhvdXIKICAgICAgICAgCiAgICAgICAgICMgYWNjb3VudGluZyBmb3IgcG90ZW50aWFsIG5lZ2F0aXZlIG9yIHplcm8gdHJhdmVsIHRpbWVzCiAgICAgICAgIFNwZWVkQXZnX01waCA9IGlmZWxzZShUcmF2ZWxUaW1lX0hyID4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pIC8gVHJhdmVsVGltZV9IciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIAogICAgICAgICBTdGFydF9JRCA9IGxhZyhTdG9wSURfQ2xlYW4pLAogICAgICAgICBTdGFydF9EZXNjID0gbGFnKFN0b3BfRGVzYyksCiAgICAgICAgIFN0YXJ0U3RvcF9JRCA9IGlmZWxzZShpcy5uYShTdGFydF9JRCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiTlVMTCIsIFN0b3BJRF9DbGVhbiwgc2VwID0gIi0tIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZShTdGFydF9JRCwgU3RvcElEX0NsZWFuLCBzZXAgPSAiLS0iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgoKcm0oQWxsRGF5c19BZGRWYXJzKQpzdHIoQWxsRGF5c19CdXNEYXkpCgojIHN1bW1hcnkoQWxsRGF5c19CdXNEYXkpCgojIFZpZXcodGFpbChBbGxEYXlzX0J1c0RheSwgNTApKQoKYGBgCgoKSW5zcGVjdGluZyBmb3IgaXNzdWVzIHdpdGggU3RhcnRTdG9wX0lEICh3aGVyZSB0aGUgdmFsdWUgaXMgZWl0aGVyIE5BIG9yIGNvbnRhaW5zIE5VTEwpLiBUaGV5IE9OTFkgZXhpc3Qgd2hlbiBCdXNEYXlfRXZlbnROdW0gPSAxICh3aGljaCBpcyBieSBkZXNpZ24pLiBTbyBldmVyeXRoaW5nIGxvb2tzIE9LLgpgYGB7cn0KClZpZXcoZ3JvdXBfYnkoQWxsRGF5c19CdXNEYXksCiAgICAgICAgICAgICAgU3RhcnRTdG9wX0lECiAgICAgICAgICAgICApICU+JSAKICAgICAgIHN1bW1hcmlzZSgKICAgICAgICAgQ250ID0gbigpCiAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhDbnQpCiAgICAgICAgICAgICAgKQogICAgKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19CdXNEYXksCiAgICAgICAgICAgIChpcy5uYShTdGFydFN0b3BfSUQpIHwKICAgICAgICAgICAgICBzdHJfZGV0ZWN0KFN0YXJ0U3RvcF9JRCwgIk5VTEwiKQogICAgICAgICAgICApICYKICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMQogICAgICAgICAgICkKICAgICkKCmBgYAoKClN0YXRzIChxdWFudGlsZXMpIG92ZXJhbGwgZm9yIFRyYXZlbERpc3RhbmNlX01pLgpgYGB7cn0KClF1YW50aWxlc19kdCA8LSBBbGxEYXlzX0J1c0RheSAlPiUgCiAgbXV0YXRlKFREX01pX3EyID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuMDIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX3E5OCA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjk4LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfcTIgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9TZWMsIHByb2JzID0gMC4wMiwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX3E5OCA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjk4LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9xMiA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuMDIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX3E5OCA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuOTgsIG5hLnJtID0gVFJVRSkKICAgICAgICApICU+JSAKICBkYXRhLnRhYmxlKCkKCgpTdGF0cyA8LSBRdWFudGlsZXNfZHQgJT4lIAogIG11dGF0ZShURF9NaV9NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVERfTWlfTWVhbl9GID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9xMiA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX01lZF9GID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX3EyIDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfcTk4XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pW1REX01pX3EyIDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfcTk4XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgCiAgICAgICAgIFRUX1NlY19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX01lYW5fRiA9IG1lYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX3EyIDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19NZWQgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfcTIgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfcTk4XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykKICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19DbnRfRiA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9TZWNbVFRfU2VjX3EyIDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX3E5OF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCgogICAgICAgICBUVF9Icl9NZWFuID0gbWVhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfSHJbVFRfSHJfcTIgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfcTk4XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX3EyIDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX0NudCA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcikKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVFRfSHJfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfSHJbVFRfSHJfcTIgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfcTk4XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lIAogIGRhdGEuZnJhbWUoKQoKcm0oQWxsRGF5c19CdXNEYXkpCnJtKFF1YW50aWxlc19kdCkKc3RyKFN0YXRzKQojIFZpZXcoaGVhZChTdGF0cywgNTApKQoKYGBgCgoKU3RhdHMgZm9yIFN0YXJ0U3RvcF9JRC4KYGBge3J9CgpRdWFudGlsZXNfU1NfZHQgPC0gZ3JvdXBfYnkoU3RhdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFREX01pX1NTX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfU1NfcTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9TZWMsIHByb2JzID0gMC4wNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19xNSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuOTUsIG5hLnJtID0gVFJVRSkKICAgICAgICApICU+JSAKICBkYXRhLnRhYmxlKCkKCgpTdGF0c19TdFN0IDwtIGdyb3VwX2J5KFF1YW50aWxlc19TU19kdCwKICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShURF9NaV9TU19NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVERfTWlfU1NfTWVhbl9GID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9TU19xNSA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9TU19NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTX01lZF9GID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NfcTk1XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgCiAgICAgICAgIFRUX1NlY19TU19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTX01lYW5fRiA9IG1lYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX1NTX3E1IDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19TU19NZWQgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU19NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfU1NfcTUgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfU1NfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfU1NfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykpLAogICAgICAgICBUVF9TZWNfU1NfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19TU19xNSA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19TU19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCgogICAgICAgICBUVF9Icl9TU19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfSHJbVFRfSHJfU1NfcTUgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfU1NfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX1NTX3E1IDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTX0NudCA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcikpLAogICAgICAgICBUVF9Icl9TU19DbnRfRiA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcltUVF9Icl9TU19xNSA8PSBUcmF2ZWxUaW1lX0hyICYgVHJhdmVsVGltZV9IciA8PSBUVF9Icl9TU19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgZGF0YS5mcmFtZSgpCgpybShTdGF0cykKcm0oUXVhbnRpbGVzX1NTX2R0KQpzdHIoU3RhdHNfU3RTdCkKIyBWaWV3KGhlYWQoU3RhdHNfU3RTdCwgNTApKQoKYGBgCgoKU3RhdHMgZm9yIFN0YXJ0U3RvcF9JRCB3aXRoIEV2ZW50X1RpbWVfSHJHcm91cC4KYGBge3J9CgpRdWFudGlsZXNfU1NIR19kdCA8LSBncm91cF9ieShTdGF0c19TdFN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShURF9NaV9TU0hHX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTSEdfcTk1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuOTUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU0hHX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbFRpbWVfU2VjLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU0hHX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU0hHX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbFRpbWVfSHIsIHByb2JzID0gMC4wNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfU1NIR19xOTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9IciwgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgKSAlPiUgCiAgZGF0YS50YWJsZSgpCgoKU3RhdHNfU3RTdF9IckdycCA8LSBncm91cF9ieShRdWFudGlsZXNfU1NIR19kdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoVERfTWlfU1NIR19NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVERfTWlfU1NIR19NZWFuX0YgPSBtZWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTSEdfcTUgPD0gVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaSA8PSBURF9NaV9TU0hHX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NIR19NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTSEdfTWVkX0YgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWlbVERfTWlfU1NIR19xNSA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFREX01pX1NTSEdfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9TU0hHX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9TU0hHX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NIR19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgCiAgICAgICAgIFRUX1NlY19TU0hHX01lYW4gPSBtZWFuKFRyYXZlbFRpbWVfU2VjLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfU1NIR19NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19TU0hHX3E1IDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19TU0hHX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTSEdfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX1NTSEdfcTUgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfU1NIR19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfU1NIR19DbnQgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSksCiAgICAgICAgIFRUX1NlY19TU0hHX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfU1NIR19xNSA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19TU0hHX3E5NV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCgogICAgICAgICBUVF9Icl9TU0hHX01lYW4gPSBtZWFuKFRyYXZlbFRpbWVfSHIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTSEdfTWVhbl9GID0gbWVhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX1NTSEdfcTUgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfU1NIR19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTSEdfTWVkID0gbWVkaWFuKFRyYXZlbFRpbWVfSHIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTSEdfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9IcltUVF9Icl9TU0hHX3E1IDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTSEdfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX0hyKSksCiAgICAgICAgIFRUX0hyX1NTSEdfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfSHJbVFRfSHJfU1NIR19xNSA8PSBUcmF2ZWxUaW1lX0hyICYgVHJhdmVsVGltZV9IciA8PSBUVF9Icl9TU0hHX3E5NV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgZGF0YS5mcmFtZSgpCgpybShTdGF0c19TdFN0KQpybShRdWFudGlsZXNfU1NIR19kdCkKc3RyKFN0YXRzX1N0U3RfSHJHcnApCiMgVmlldyhoZWFkKFN0YXRzX1N0U3RfSHJHcnAsIDUwKSkKCmBgYAoKCkZlYXR1cmUgZW5naW5lZXJpbmcuCgpDcmVhdGluZyBhIEJ1c0V2ZW50Um91dGUgcm93IG51bWJlciwgYW5kIGEgUm91dGVBbHRfTGFnMSBpbmRpY2F0b3IgZm9yIGZ1dHVyZSBpZGVudGlmaWNhdGlvbiBwdXJwb3Nlcy4gCmBgYHtyfQoKIyBybShRdWFudGlsZXNfZHQpCiMgcm0oUXVhbnRpbGVzX1NTX2R0KQojIHJtKEFsbERheXNfQnVzRGF5KQojIHJtKFF1YW50aWxlc19TU0hHX2R0KQojIHJtKFN0YXRzX1N0U3QpCgojIEFsbERheXNfQnVzRGF5Um91dGUgPC0gZ3JvdXBfYnkoU3RhdHNfU3RTdF9IckdycCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJ1c19JRCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF0ZSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvdXRlCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAojICAgbXV0YXRlKFJvdXRlQWx0X0xhZzIgPSBsYWcoUm91dGVBbHQpICAjIHVzZWQgaW4gZnV0dXJlIGFuYWx5c2VzIHRvIGlkZW50aWZ5IFJvdXRlQWx0IChkaXJlY3Rpb24pIGNoYW5nZXMKIyAgICAgICAgICAKIyAgICAgICAgICAjIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEgPSBsYWcoT2RvbWV0ZXJfRGlzdGFuY2UpLAojICAgICAgICAgICMgCiMgICAgICAgICAgIyAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSBkaXN0YW5jZXMKIyAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX0Z0ID0gaWZlbHNlKE9kb21ldGVyX0Rpc3RhbmNlID49IE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZSAtIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWkgPSBUcmF2ZWxEaXN0YW5jZV9GdCAvIDUyODAsICM1LDI4MCBmZWV0IGluIDEgbWlsZQojICAgICAgICAgICMgCiMgICAgICAgICAgIyAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSB0aW1lcwojICAgICAgICAgICMgVHJhdmVsVGltZV9TZWMgPSBhcy5udW1lcmljKGlmZWxzZShFdmVudF9UaW1lID49IGxhZyhEZXBhcnR1cmVfVGltZSksCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUgLSBsYWcoRGVwYXJ0dXJlX1RpbWUpLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiMgICAgICAgICAgIyBUcmF2ZWxUaW1lX0hyID0gVHJhdmVsVGltZV9TZWMgLyAzNjAwLCAjIDMsNjAwIHNlY29uZHMgaW4gMSBob3VyCiMgICAgICAgICAgIyAKIyAgICAgICAgICAjICMgYWNjb3VudGluZyBmb3IgcG90ZW50aWFsIG5lZ2F0aXZlIG9yIHplcm8gdHJhdmVsIHRpbWVzCiMgICAgICAgICAgIyBTcGVlZEF2Z19NcGggPSBpZmVsc2UoVHJhdmVsVGltZV9IciA+IDAsCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgLyBUcmF2ZWxUaW1lX0hyLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgIE5BCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICApICU+JSAKIyAgIGRhdGEuZnJhbWUoKQojIAojIHJtKFN0YXRzX1N0U3RfSHJHcnApCiMgc3RyKEFsbERheXNfQnVzRGF5Um91dGUpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ2FsY3VsYXRpbmcgYSB2YXJpYWJsZSB0byBrbm93IGlmIHRoZSBSb3V0ZUFsdCBjaGFuZ2VkLiBDb3VsZCBiZSB1c2VmdWwgaW4gaGVscGluZyBpZGVudGlmeWluZyB3ZWlyZG5lc3MgaW4gY2FsY3VsYXRlZCBkaXN0YW5jZXMgYW5kIHNwZWVkcy4KYGBge3J9CgojIHJtKFN0YXRzX1N0U3RfSHJHcnApCgpBbGxEYXlzX0RpckNoYW5nZSA8LSBTdGF0c19TdFN0X0hyR3JwICU+JSAgIyBBbGxEYXlzX0J1c0RheVJvdXRlICU+JSAKICBtdXRhdGUoUnRlQ2hhbmdlID0gaWZlbHNlKFJvdXRlID09IFJvdXRlX0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIgogICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBSdGVDaGFuZ2UyID0gZmFjdG9yKGlmZWxzZShpcy5uYShSdGVDaGFuZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnRlQ2hhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgRGlyQ2hhbmdlID0gaWZlbHNlKFJvdXRlQWx0ID09IFJvdXRlQWx0X0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIgogICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBEaXJDaGFuZ2UyID0gZmFjdG9yKGlmZWxzZShpcy5uYShEaXJDaGFuZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGlyQ2hhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkKCiMgcm0oQWxsRGF5c19CdXNEYXlSb3V0ZSkKcm0oU3RhdHNfU3RTdF9IckdycCkKc3RyKEFsbERheXNfRGlyQ2hhbmdlKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19EaXJDaGFuZ2UsCiAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAyNTcwMDYwLCAyNTcwMDgwKQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkKICAgICkKCmBgYAoKClJlLW9yZGVyaW5nIHRoZSB2YXJpYWJsZXMgdG8gZWFzZSB3aXRoIGNvbXByZWhlbnNpb24uCmBgYHtyfQoKQWxsRGF5c19OZXdPcmRlciA8LSAgc2VsZWN0KEFsbERheXNfRGlyQ2hhbmdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVW5pcXVlTGF0TG5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ0ZUNoYW5nZTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZUFsdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUm91dGVBbHRfTGFnMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZV9EaXJlY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wX1NlcXVlbmNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydF9EZXNjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdG9wX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcElEX0luZGljYXRvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfRGVzYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50cnlDb2RlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9TdGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfQ291bnR5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9DaXR5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UeXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfRGVzY3JpcHRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX1lyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9NdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZXBhcnR1cmVfVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIER3ZWxsX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEd2VsbF9UaW1lMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBMYXRpdHVkZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvbmdpdHVkZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhlYWRpbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX0Z0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfcTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9xOTgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfcTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX3EyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX3E5OCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19xOTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfTWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX0NudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9xMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX3E5OCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX3E1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfcTk1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTSEdfcTk1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfTWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfTWVkX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTSEdfTWVkX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX0NudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX0NudF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX0NudF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnJtKEFsbERheXNfRGlyQ2hhbmdlKQpzdHIoc2VsZWN0KEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgKQogICApCnN0cihBbGxEYXlzX05ld09yZGVyKQoKIyBWaWV3KGhlYWQoQWxsRGF5c19OZXdPcmRlciwgNTAwKSkKIyBWaWV3KHRhaWwoQWxsRGF5c19OZXdPcmRlciwgNTAwKSkKCmBgYAoKClN1bW1hcml6aW5nIHRoZSBkYXRhIHRvIGhlbHAgc3BvdCBhbm9tb2xpZXMuCmBgYHtyfQoKVmlldyhncm91cF9ieShBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgIFN0b3BfQ2l0eSkgJT4lIAogICAgICAgc3VtbWFyaXNlKENudF9OdW0gPSBuKCksCiAgICAgICAgICAgICAgICAgQ250X1BjdCA9IDEwMCpDbnRfTnVtIC8gKG5yb3coQWxsRGF5c19OZXdPcmRlcikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhDbnRfTnVtKSkKKQoKc3VtbWFyeShBbGxEYXlzX05ld09yZGVyKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKTogOTklIG9mIFRyYXZlbERpc3RhbmNlX01pIGFyZSBhYm91dCAxIG1pbGUgb3IgbGVzcy4uLmJ1dCBzb21lIHdlaXJkIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcyAoZS5nLiwgNTg0IG1pbGVzIHRyYXZlbGVkKSBleGlzdC4KYGBge3J9CgpUcmF2RGlzdE1pX050aWxlIDwtIGFzLmRhdGEuZnJhbWUoQWxsRGF5c19OZXdPcmRlciRUcmF2ZWxEaXN0YW5jZV9NaSkgJT4lIAogIG11dGF0ZSgjUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdPcmRlciRUcmF2ZWxEaXN0YW5jZV9NaSwgMTAwKSwKICAgICAgICAgI01pblIgPSBtaW5fcmFuayhBbGxEYXlzX05ld09yZGVyJFRyYXZlbERpc3RhbmNlX01pKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld09yZGVyJFRyYXZlbERpc3RhbmNlX01pKSwKICAgICAgICAgUGN0Ul9Sb3VuZCA9IHJvdW5kKFBjdFIsIDIpCiAgICAgICAgKSAKCmNvbG5hbWVzKFRyYXZEaXN0TWlfTnRpbGUpWzFdIDwtICJUcmF2ZWxEaXN0YW5jZV9NaSIKIyBzdHIoVHJhdkRpc3RNaV9OdGlsZSkKClRyYXZEaXN0TWlfTnRpbGVfUm93cyA8LSBucm93KFRyYXZEaXN0TWlfTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pX050aWxlLCA1MDApKQoKClRyYXZEaXN0TWlfUGN0aWxlcyA8LSBncm91cF9ieShUcmF2RGlzdE1pX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGN0Ul9Sb3VuZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5UcmF2RGlzdE1pQXRQY3RpbGUgPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWkpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gVHJhdkRpc3RNaV9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKEN1bVN1bVBBdFAgPSBjdW1zdW0oUGN0c0F0UGN0aWxlKQogICAgICAgICkKCnJtKFRyYXZEaXN0TWlfTnRpbGUpCnJtKFRyYXZEaXN0TWlfTnRpbGVfUm93cykKClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKQpUcmF2RGlzdE1pX1BjdGlsZXMKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkuCgpXaHkgYXJlIHNvbWUgVHJhdmVsRGlzdGFuY2VfTWkgIk5BIj8gSXQgbG9va3MgbGlrZSBwYXJ0aWFsbHkgYmVjYXVzZSB0aGUgcmVjb3JkcyBhcmUgdGhlIGZpcnN0IHRyaXAgb2YgdGhlIGRheSAoZm9yIHRoYXQgYnVzKSwgc28gSSBwdXJwb3NlZnVsbHkgc2V0IHRoZSBkaXN0YW5jZSB0byAiTkEiLiBBbm90aGVyIHJlYXNvbiBpcyBkdWUgdG8gdGhlIG9kb21ldGVyIHJlY29yZGluZyBhIHZhbHVlIGxlc3MgdGhhbiB0aGUgcHJldmlvdXMgb2RvbWV0ZXIgcmVjb3JkaW5nLiBJbiBtb3N0IGNhc2VzLCBJIGhhdmUgbm8gZXhwbGFuYXRpb24gZm9yIHRoaXMgLSB0aG91Z2ggSSBoYXZlIG9ic2VydmVkIGFib3V0IDY3JSBvZiBhbGwgaW5zdGFuY2VzIHdoZXJlIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BIChvdGhlciB0aGFuIGJlY2F1c2UgaXQncyB0aGUgZmlyc3QgcmVjb3JkIG9mIHRoZSBkYXkpIGFyZSBpbnN0YW5jZXMgd2hlcmUgRGlyQ2hhbmdlMiBpcyAiQ2hhbmdlIi4gVGhpcyBpcyB3ZWlyZCBhbmQgc2hvdWxkIGJlIGFza2VkIHRvIFdNQVRBLgpgYGB7cn0KCiMgVmlldyhoZWFkKEFsbERheXNfTmV3T3JkZXIsIDUwMCkpCgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAjIFdoZW4gQnVzRGF5X0V2ZW50TnVtID09IDEsIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BIGJ5IGRlc2lnbiAoZG9uJ3Qgd2FudCB0byBjYWxjdWxhdGUgZGlzdGFuY2UgYmFzZWQgb24geWVzdGVyZGF5J3MgcG9zaXRpb24pCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBncm91cF9ieShTdGFydFN0b3BfSUQpICU+JSAKICAgICAgIHN1bW1hcmlzZShDbnRzID0gc3VtKGlzLm5hKFRyYXZlbERpc3RhbmNlX01pKQogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhDbnRzKQogICAgICAgICAgICAgICkKICAgICkKClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIFN0YXJ0U3RvcF9JRCA9PSAiMTAwMDI0NS0tMTAwMDIxMSIKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdChSb3dOdW1fT0csCiAgICAgICAgICAgICAgU3RhcnRTdG9wX0lELAogICAgICAgICAgICAgIEV2ZW50X1RpbWUsCiAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwLAogICAgICAgICAgICAgIEJ1c19JRCwKICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgIFREX01pX1NTX01lYW4sCiAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbl9GLAogICAgICAgICAgICAgIFREX01pX1NTSEdfTWVhbiwKICAgICAgICAgICAgICBURF9NaV9TU0hHX01lYW5fRiwKICAgICAgICAgICAgICBURF9NaV9TU19NZWQsCiAgICAgICAgICAgICAgVERfTWlfU1NfTWVkX0YsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWQsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWRfRiwKICAgICAgICAgICAgICBURF9NaV9TU19DbnQsCiAgICAgICAgICAgICAgVERfTWlfU1NfQ250X0YsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnQsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnRfRgogICAgICAgICAgICAgICkgJT4lIAogICAgICAgbXV0YXRlKFJhdGlvX01lYW5Ub0h2cnMgPSBURF9NaV9TU19NZWFuIC8gVHJhdmVsRGlzdGFuY2VfTWlfSHZycykgJT4lIAogICAgICAgYXJyYW5nZShFdmVudF9UaW1lKQogICAgKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgKQogICAgKQoKIyBUaGVzZSByZWNvcmRzIGFyZSBOQSBiZWN1YXNlIHRoZSByZWNvcmQgaXMgdGhlIGZpcnN0IHJlY29yZCBvZiB0aGUgZGF5ICh0aGUgRXZlbnRfVGltZV9EYXRlKQpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMzI2LCAzNDYpIHwgIyAzMzYKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNTkxLCA2MTEpIHwgIyA2MDEKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgODQ1LCA4NjUpICMgODU1CiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KClRoZXNlIHJlY29yZHMgYXJlIE5BIGJlY3Vhc2UgdGhlIGN1cnJlbnQgcmVjb3JkIG9kb21ldGVyIGlzIGxlc3MgdGhhbiB0aGUgcHJldmlvdXMgcmVjb3JkIG9kb21ldGVyLiBUaGVvcmV0aWNhbGx5LCB0aGlzIHNob3VsZCBOT1QgaGFwcGVuLiBNZTogaXQgYXBwZWFycyB0aGF0IGFib3V0IDY3JSBvZiBhbGwgaW5zdGFuY2VzIHdoZXJlIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BIChvdGhlciB0aGFuIGJlY2F1c2UgaXQncyB0aCBmaXJzdCByZWNvcmQgb2YgdGhlIGRheSkgYXJlIGluc3RhbmNlcyB3aGVyZSBEaXJDaGFuZ2UyIGlzICJDaGFuZ2UiLiBUaGlzIGlzIHdlaXJkIGFuZCBzaG91bGQgYmUgYXNrZWQgdG8gV01BVEEuCmBgYHtyfQoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDE5NCwgMjE0KSB8ICMgMjA0CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ0MCwgNDYwKSB8ICMgNDUwCiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ3OCwgNDk4KSB8ICMgNDg4CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDUxMCwgNTMwKSAjIDUyMAogICAgICAgICAgICkKICAgICkKClRlc3RUYWJsZSA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMQogICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFRyYXZlbERpc3RhbmNlX05BID0gYXMuZmFjdG9yKGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJ1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmFsc2UiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUKICBncm91cF9ieShEaXJDaGFuZ2UyLCBUcmF2ZWxEaXN0YW5jZV9OQSkgJT4lCiAgc3VtbWFyaXNlKFRyYXZEaXN0TWlfTkFDbnRzID0gbigpCiAgICAgICAgICAgKQoKIyBUZXN0VGFibGUKClRlc3RUYWJsZV9TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoVGVzdFRhYmxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX05BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZEaXN0TWlfTkFDbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChGYWxzZSwKICAgICAgICAgVHJ1ZQogICAgICAgICkKCnJvdy5uYW1lcyhUZXN0VGFibGVfU3ByZWFkKSA8LSBjKCJDaGFuZ2UiLCAiU2FtZSIpCiMgc3RyKFRlc3RUYWJsZV9TcHJlYWQpCiMgVGVzdFRhYmxlX1NwcmVhZAoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVGVzdFRhYmxlX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVGVzdFRhYmxlX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMgogICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxldCdzIGxvb2sgYXQganVzdCB0aGUgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIE5PVCAiTkEiLgpgYGB7cn0KCnJtKFRlc3RUYWJsZSwgVGVzdFRhYmxlX1NwcmVhZCkKClRyYXZlbERpc3RhbmNlX01pX05vTkEgPC0gZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWkgIT0gMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpkaW0oQWxsRGF5c19OZXdPcmRlcikKZGltKFRyYXZlbERpc3RhbmNlX01pX05vTkEpCm5yb3coQWxsRGF5c19OZXdPcmRlcikgLSBucm93KFRyYXZlbERpc3RhbmNlX01pX05vTkEpCgpzdHIoVHJhdmVsRGlzdGFuY2VfTWlfTm9OQSkKc3VtbWFyeShUcmF2ZWxEaXN0YW5jZV9NaV9Ob05BKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxldCdzIHBsb3QganVzdCB0aGUgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIE5PVCAiTkEiLgpgYGB7cn0KClRyYXZEaXN0TWlfSGlzdERlbiA8LSBnZ3Bsb3Qoc2VsZWN0KFRyYXZlbERpc3RhbmNlX01pX05vTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDUsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMS41KSwgeWxpbSA9IGMoMCwgNC4wKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiVmFyaWF0aW9uIGluIERpc3RhbmNlIEJldHdlZW4gU3RvcHMiLAogICAgICAgeCA9ICJUcmF2ZWwgRGlzdGFuY2UgKG1pbGVzKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiCiAgICAgICkKClRyYXZEaXN0TWlfSGlzdERlbgoKYGBgCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pLgoKTG9va2luZyBhdCB0aGUgZXh0cmVtZWx5IGxhcmdlIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcy4gU29tZSAoYXByb3ggMjclKSBvZiBUcmF2ZWxEaXN0YW5jZV9NaSB2YWx1ZXMgPiAxIG1pbGUgYXJlIHdoZW4gdGhlIERpckNoYW5nZTIgY2hhbmdlcy4uLmJ1dCB3aGF0IGFib3V0IHRoZSBvdGhlciB+NzMlPwpgYGB7cn0KCnJtKFRyYXZlbERpc3RhbmNlX01pX05vTkEpCgojIGV4YW1wbGVzIG9mIHdlaXJkbHkgbGFyZ2UgVHJhdmVsRGlzdGFuY2VfTWkKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgPiAxLjE1ODcxMjEyMTIgIyAxLjE1ODcxMjEyMTIgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbERpc3RhbmNlX01pKQogICAgICAgICAgICAgICkKICAgICkKCgojIFdoeSBhcmUgdGhlc2UgZXh0cmVtZXM/ICBBaXJwb3J0cz8gIEJ1cyBjb2xsZWN0aW9uIHBvaW50cz8KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0MDQ0LCA0OTQwNjQpIHwgIyA0OTQwNTQKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0MjczLCA0OTQyOTMpIHwgIyA0OTQyODMKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0NjI2LCA0OTQ2NDYpIHwgIyA0OTQ2MzYKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMTYxMDE1NiwgMTYxMDE3NikgfCAjIDE2MTAxNjYKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMjA3MzA3NCwgMjA3MzA5NCkgIyAyMDczMDg0CiAgICAgICAgICAgKQogICAgKQoKIyBCZWZvcmUgUmVtb3ZpbmcgUnVucwojIFZpZXcoZmlsdGVyKEFsbERheXNfU29ydGVkLAojICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0OTQwNDQsIDQ5NDA2NCkgfCAjIDQ5NDA1NAojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDI3MywgNDk0MjkzKSB8ICMgNDk0MjgzCiMgICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0NjI2LCA0OTQ2NDYpIHwgIyA0OTQ2MzYKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAxNjEwMTU2LCAxNjEwMTc2KSB8ICMgMTYxMDE2NgojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDIwNzMwNzQsIDIwNzMwOTQpICMgMjA3MzA4NAojICAgICAgICAgICAgKQojICAgICApCgojIEFmdGVyIFJlbW92aW5nIFJ1bnMKIyBWaWV3KGZpbHRlcihBbGxEYXlzX0ZpcnN0U3RvcElELAojICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0OTQwNDQsIDQ5NDA2NCkgfCAjIDQ5NDA1NAojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDI3MywgNDk0MjkzKSB8ICMgNDk0MjgzCiMgICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0NjI2LCA0OTQ2NDYpIHwgIyA0OTQ2MzYKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAxNjEwMTU2LCAxNjEwMTc2KSB8ICMgMTYxMDE2NgojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDIwNzMwNzQsIDIwNzMwOTQpICMgMjA3MzA4NAojICAgICAgICAgICAgKQojICAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pLgoKQW55IHJlbGF0aW9uIHdpdGggRGlyQ2hhbmdlMj8gIERvZXNuJ3QgbG9vayBhcyBpZiB0aGlzIGlzIHNvLgpgYGB7cn0KCkV4dHJlbWVUcmF2RGlzdCA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAhaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoVHJhdkRpc3RfRXh0cmVtZSA9IGlmZWxzZShUcmF2ZWxEaXN0YW5jZV9NaSA+IDEuMTU4NzEyMTIxMiwgIyAxLjE1ODcxMjEyMTIgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcnVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmFsc2UiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgZ3JvdXBfYnkoRGlyQ2hhbmdlMiwgVHJhdkRpc3RfRXh0cmVtZSkgJT4lIAogIHN1bW1hcmlzZShUcmF2RGlzdE1JX0V4dENudHMgPSBuKCkKICAgICAgICAgICApCgojIEV4dHJlbWVUcmF2RGlzdAoKCkV4dHJlbWVUcmF2RGlzdF9TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoRXh0cmVtZVRyYXZEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZEaXN0X0V4dHJlbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdkRpc3RNSV9FeHRDbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChGYWxzZSwKICAgICAgICAgVHJ1ZQogICAgICAgICkKCnJvdy5uYW1lcyhFeHRyZW1lVHJhdkRpc3RfU3ByZWFkKSA8LSBjKCJDaGFuZ2UiLCAiU2FtZSIpCiMgc3RyKEV4dHJlbWVUcmF2RGlzdF9TcHJlYWQpCiMgRXh0cmVtZVRyYXZEaXN0X1NwcmVhZAoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoRXh0cmVtZVRyYXZEaXN0X1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoRXh0cmVtZVRyYXZEaXN0X1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMgogICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxvb2tpbmcgYXQgc3BlY2lmaWMgYnVzZXMgYW5kIFN0YXJ0U3RvcF9JRC4KYGBge3J9CgpybShFeHRyZW1lVHJhdkRpc3QsIEV4dHJlbWVUcmF2RGlzdF9TcHJlYWQpCgpWaWV3KGFycmFuZ2UoZ3JvdXBfYnkoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgIEJ1c19JRAogICAgICAgICAgICAgICAgICAgICApICU+JSAKICAgICAgICAgICAgICAgc3VtbWFyaXNlKERpc3RUcmF2X01lYW4gPSBtZWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgRGlzdFRyYXZfTWVkID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICBkZXNjKERpc3RUcmF2X01lZCkKICAgICAgICAgICAgKQogICAgKQoKCiMgZXhhbXBsZSBvZiBleHRyZW1lbHkgc21hbGwgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIChsb29rcyBsaWtlIHRoZSBvZG9tZXRlciB3YXNuJ3QgZnVuY3Rpb25pbmcpClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIEJ1c19JRCA9PSA2MTExIHwKICAgICAgICAgICAgICBCdXNfSUQgPT0gNzIwMSB8CiAgICAgICAgICAgICAgQnVzX0lEID09IDgwNTgKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoQnVzX0lELCBFdmVudF9UaW1lKQogICAgKQoKClZpZXcoYXJyYW5nZShncm91cF9ieShBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lECiAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICBzdW1tYXJpc2UoRGlzdFRyYXZfTWVhbiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICBEaXN0VHJhdl9NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgIGRlc2MoRGlzdFRyYXZfTWVkKQogICAgICAgICAgICApCiAgICApCgojIGV4YW1wbGUgb2YgZXh0cmVtZWx5IGxhcmdlIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcy4uLm5vIGlkZWEgd2h5Li4uClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIFN0YXJ0U3RvcF9JRCA9PSAiMTAwMzY2NS0tMTIiIHwKICAgICAgICAgICAgICBTdGFydFN0b3BfSUQgPT0gIjEwMDM2NjUtLTUwMDE5MjUiIHwKICAgICAgICAgICAgICBTdGFydFN0b3BfSUQgPT0gIjMwMDEwMzgtLTMwMDI1NjUiCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFN0YXJ0U3RvcF9JRCwgRXZlbnRfVGltZSkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpJZiBUcmF2ZWxEaXNudGFjZV9NaSBpcyBiZWxvdyB0aGUgNXRoIHBlcmNlbnRpbGUgZm9yIHRoYXQgU3RhcnRTdG9wX0lELCBvciBpZiBUcmF2ZWxEaXNudGFjZV9NaSBpcyBhYm92ZSB0aGUgOTV0aCBwZXJjZW50aWxlIGZvciB0aGF0IFN0YXJ0U3RvcF9JRCwgb3IgaWYgVHJhdmVsRGlzdGFuY2VfTWkgaXMgTkEgKHdoZW4gdGhlIEJ1c0RheV9FdmVudE51bSAhPTEpLCBjb25zaWRlciB0aGlzIGFuIG91dGxpZXIuICBJbiB0aGlzIGNhc2UsIHJlcGxhY2UgdGhlIHZhbHVlIHdpdGggdGhlIG1lYW4gZm9yIHRoYXQgU3RhcnRTdG9wX0lEIGFuZCBIb3VyR3JvdXAgKFREX01pX1NTSEdfTWVhbl9GKSwgb3IgaWYgdGhlcmUgYXJlIG5vdCBlbm91Z2ggdmFsdWVzIGF0IHRoZSBIb3VyR3JvdXAgbGV2ZWwsIHJlcGxhY2UgaXQgd2l0aCB0aGUgbWVhbiBmb3IgdGhhdCBTdGFydFN0b3BfSUQuCmBgYHtyfQoKIyBWaWV3KHRhaWwoQWxsRGF5c19OZXdPcmRlciwgNTAwKSkKCkFsbERheXNfTmV3VHJhdmVsRGlzdCA8LSAKICBtdXRhdGUoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3ID0gaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX0NudF9GID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsRGlzdGFuY2VfTWkgPCBURF9NaV9TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgPiBURF9NaV9TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX0NudCA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgIT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgPT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkpKSwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3X0xhYmVsID0gCiAgICAgICAgICAgZmFjdG9yKGlmZWxzZSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVERfTWlfU1NIR19NZWFuX0YiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaSA8IFREX01pX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA+IFREX01pX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJURF9NaV9TU19NZWFuX0YiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaSA8IFREX01pX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA+IFREX01pX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250ID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlREX01pX1NTX01lYW4iLAogICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhdmVsRGlzdGFuY2VfTWlfSHZycyIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgPT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJURF9NaV9TU19NZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJUcmF2ZWxEaXN0YW5jZV9NaSIKICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSkKICAgICAgICAgICAgICAgICApLAogICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzID0gaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pX05ldyA8IFREX01pX3EyIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA+IFREX01pX3E5OAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCA9CiAgICAgICAgICAgZmFjdG9yKGlmZWxzZSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfSHZycykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsRGlzdGFuY2VfTWlfTmV3IDwgVERfTWlfcTIgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgPiBURF9NaV9xOTgKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICJUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihUcmF2ZWxEaXN0YW5jZV9NaV9OZXdfTGFiZWwpCiAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICApLAogICAgICAgICBTcGVlZEF2Z19NcGhfTmV3SHZycyA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMgLyBUcmF2ZWxUaW1lX0hyCiAgICAgICAgKQoKcm0oQWxsRGF5c19OZXdPcmRlcikKc3RyKEFsbERheXNfTmV3VHJhdmVsRGlzdCkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LgoKUXVpY2sgc3VtbWFyeSBhbmQgdGhlbiBjb3JyZWxhdGlvbiBjYWxjdWxhdGlvbi4KYGBge3J9CgojIDM4IHJvd3MgbWVldCB0aGlzIGNyaXRlcmlhIGFueW1vcmUgIC0tICBhcHBlYXJzIHRvIGJlIHRoZSBjYXNlIHdoZW4gYm90aCB0aGUgTGF0IExvbmcgY2FsY3VsYXRpb25zLCBhbmQgdGhlIFRyYXZlbERpc3RhbmNlIGNhbGN1bGF0aW9ucyBkaWQgbm90IGZ1bmN0aW9uIHByb3Blcmx5LgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIGlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ldykgJgogICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgKQogICAgKQoKVmlldyhBbGxEYXlzX05ld1RyYXZlbERpc3QgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbERpc3RhbmNlX01pX05ldykpICU+JSAKICAgICAgIGhlYWQoNTAwKQogICAgKQoKc3VtbWFyeShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICApCiAgICAgICApCgoKY29yKHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWksCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycwogICAgICAgICAgKSwKICAgIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiCiAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzX0xhYmVsICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbC4KClNob3cgaG93IHRoZSBsYWJlbHMgY2hhbmdlZC4KYGBge3J9Cgpncm91cF9ieShBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbAogICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShDbnROdW0gPSBuKCksCiAgICAgICAgICAgIENudFBjdCA9IGZvcm1hdChDbnROdW0gLyBucm93KEFsbERheXNfTmV3VHJhdmVsRGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2llbnRpZmljID0gOTk5OQogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShkZXNjKENudFBjdCkKICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgJiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpHcmFwaGluZyB0aGUgdHdvIG1ldGhvZHMgb2YgY2FsY3VsYXRpbmcgVHJhdmVsRGlzdGFuY2VfTWkuCgpGaXJzdCwgbGV0J3MgZ2V0IGNyZWF0ZSBhIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIGxpbmVyIG1vZGVsIGVxdWF0aW9uLgpgYGB7cn0KCmxtX2VxbiA8LSBmdW5jdGlvbihkZiwgeSwgeCl7CiAgbSA8LSBsbSh5IH4geCwgZGYpCiAgCiAgbCA8LSBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVsxXSwgZGlnaXRzID0gMiksCiAgICAgICAgICAgIGIgPSBmb3JtYXQoYWJzKGNvZWYobSlbMl0pLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgczEgPSBpZmVsc2UodGVzdCA9IGNvZWYobSlbMl0gPiAwLAogICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiKyIsCiAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gIi0iCiAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsCiAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDMKICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgKQogIAogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKHkpID09IGF+fnMxfn5iICUuJSBpdGFsaWMoeCkqIiwifn5pdGFsaWMocileMn4iPSJ+cjIsCiAgICAgICAgICAgICAgICAgICBsCiAgICAgICAgICAgICAgICAgICkKICAKICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkKICAgICAgICAgICAgICApICAgICAgICAgICAgIAp9CgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycy4KClNjYXR0ZXIgcGxvdCAodXNpbmcgYSAxMCUgc2FtcGxlIHRvIG1ha2luZyBwbG90dGluZyB0aW1lIGZhc3RlciBhbmQgdG8gcmVkdWNlIHVuLW5lZWRlZCBkYXRhIGluIHRoZSAic2FtZSIgc3Bsb3QpLgpgYGB7cn0KCnNldC5zZWVkKDEyMzQ1Njc4OSkKQWxsRGF5c19OZXdUcmF2ZWxEaXN0XzEwUGN0IDwtIGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHJlbmFtZShEaXN0TWV0aG9kID0gVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCkgJT4lIAogIHNhbXBsZV9mcmFjKDAuMSkKCgpUcmF2RGlzdF9NaVZzQ2FsYyA8LSBnZ3Bsb3Qoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdF8xMFBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpc3RNZXRob2QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IERpc3RNZXRob2QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCJibHVlIiwgImdyZWVuIiwgIm9yYW5nZSIsICJibGFjayIpCiAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxLCBhbHBoYSA9IDAuNSkgKwogIHNjYWxlX3NoYXBlKHNvbGlkID0gRkFMU0UpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvdXIgPSAiYmx1ZSIpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEuNSksIHlsaW0gPSBjKDAsIDEuNSkKICAgICAgICAgICAgICAgICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEuNSwgMC4yNSkKICAgICAgICAgICAgICAgICAgICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEuNSwgMC4yNSkKICAgICAgICAgICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgI2MoMC44NSwgMC40MCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpCiAgICAgICApICsKICBhbm5vdGF0ZShsYWJlbCA9IGxtX2VxbihkZiA9IEFsbERheXNfTmV3VHJhdmVsRGlzdF8xMFBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gQWxsRGF5c19OZXdUcmF2ZWxEaXN0XzEwUGN0JFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycwogICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAjIHggPSA2MiwKICAgICAgICAgICAjIHkgPSAyMCwKICAgICAgICAgICB4ID0gMC43MCwKICAgICAgICAgICB5ID0gMC4wMCwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJibHVlIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGFubm90YXRlKGxhYmVsID0gIlJlZmVyZW5jZSBMaW5lIChzbG9wZSA9IDEpIiwKICAgICAgICAgICAjIHggPSAxNiwKICAgICAgICAgICAjIHkgPSAzMCwKICAgICAgICAgICB4ID0gMC44MCwKICAgICAgICAgICB5ID0gMS4wNSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJyZWQiCiAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIlRyYXZlbERpc3RhbmNlX01pIHZzLiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIiwKICAgICAgIHggPSAiVHJhdmVsRGlzdGFuY2VfTWkiLAogICAgICAgeSA9ICJUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIgogICAgICApCiMgKwojICAgZ2VvbV9qaXR0ZXIoKQoKVHJhdkRpc3RfTWlWc0NhbGMKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LgoKR3JhcGhpbmcgdGVzdCB3aXRoIHJib2tlaC4KYGBge3J9CgpUcmF2RGlzdF9NaVZzQ2FsY19Cb2tlaCA8LSBmaWd1cmUoZGF0YSA9IHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEaXN0TWV0aG9kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsaW0gPSBjKDAsIDEuNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5bGltID0gYygwLCAxLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2xvY2F0aW9uID0gImJvdHRvbV9yaWdodCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbHlfcG9pbnRzKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgeSA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgIGNvbG9yID0gRGlzdE1ldGhvZCwKICAgICAgICAgICAgaG92ZXIgPSBjKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsIFRyYXZlbERpc3RhbmNlX01pLCBEaXN0TWV0aG9kKQogICAgICAgICAgICkgJT4lIAogIGx5X2FibGluZShhID0gMCwgYiA9IDEsIGNvbG9yID0gInJlZCIpCgpUcmF2RGlzdF9NaVZzQ2FsY19Cb2tlaAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpDYWxjdWxhdGluZyB0aGUgbWluaW11bSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgdmFsdWUgYXQgZWFjaCBwZXJjZW50aWxlLgpgYGB7cn0KCnJtKFRyYXZEaXN0X01pVnNDYWxjX0Jva2VoKQpybShBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QpCgoKc3VtbWFyeShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICApCiAgICAgICApCgoKVHJhdkRpc3RNaU5fTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzX0xhYmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFBjdFJfTiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSwKICAgICAgICAgIyBQY3RSX0ggPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpLAogICAgICAgICBQY3RSX1JvdW5kX04gPSByb3VuZChQY3RSX04sIDIpCiAgICAgICAgICMgUGN0Ul9Sb3VuZF9IID0gcm91bmQoUGN0Ul9ILCAyKQogICAgICAgICkgCgojIHN0cihUcmF2RGlzdE1pTl9OdGlsZSkKIyBWaWV3KGhlYWQoVHJhdkRpc3RNaU5fTnRpbGUsIDUwMCkpCgpUcmF2RGlzdE1pTl9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdkRpc3RNaU5fTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pTl9OdGlsZSwgNTAwKSkKCgpUcmF2RGlzdE1pTl9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZEaXN0TWlOX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmRfTgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluVERNaUF0UGN0aWxlX04gPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSwKICAgICMgTWluVERNaUF0UGN0aWxlX0ggPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICBDbnRzQXRQY3RpbGVfTiA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSksCiAgICAjIENudHNBdFBjdGlsZV9IID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSksCiAgICBQY3RzQXRQY3RpbGVfTiA9IENudHNBdFBjdGlsZV9OIC8gVHJhdkRpc3RNaU5fTnRpbGVfUm93cwogICAgIyBQY3RzQXRQY3RpbGVfSCA9IENudHNBdFBjdGlsZV9IIC8gVHJhdkRpc3RNaU5fTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQX04gPSBjdW1zdW0oUGN0c0F0UGN0aWxlX04pCiAgICAgICAgICMgQ3VtU3VtUEF0UF9IID0gY3Vtc3VtKFBjdHNBdFBjdGlsZV9IKQogICAgICAgICkKCiMgVmlldyhUcmF2RGlzdE1pTl9QY3RpbGVzKQpUcmF2RGlzdE1pTl9QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKCkNhbGN1bGF0aW5nIHRoZSBtaW5pbXVtIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMgdmFsdWUgYXQgZWFjaCBwZXJjZW50aWxlLgpgYGB7cn0KClRyYXZEaXN0TWlIX050aWxlIDwtIGFzLmRhdGEuZnJhbWUoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoIyBQY3RSX04gPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbERpc3RhbmNlX01pX05ldyksCiAgICAgICAgIFBjdFJfSCA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICAgICAgICMgUGN0Ul9Sb3VuZF9OID0gcm91bmQoUGN0Ul9OLCAyKSwKICAgICAgICAgUGN0Ul9Sb3VuZF9IID0gcm91bmQoUGN0Ul9ILCAyKQogICAgICAgICkgCgojIHN0cihUcmF2RGlzdE1pSF9OdGlsZSkKIyBWaWV3KGhlYWQoVHJhdkRpc3RNaUhfTnRpbGUsIDUwMCkpCgpUcmF2RGlzdE1pSF9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdkRpc3RNaUhfTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pSF9OdGlsZSwgNTAwKSkKCgpUcmF2RGlzdE1pSF9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZEaXN0TWlIX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmRfSAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgIyBNaW5URE1pQXRQY3RpbGVfTiA9IG1pbihUcmF2ZWxEaXN0YW5jZV9NaV9OZXcpLAogICAgTWluVERNaUF0UGN0aWxlX0ggPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICAjIENudHNBdFBjdGlsZV9OID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXcpKSwKICAgIENudHNBdFBjdGlsZV9IID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSksCiAgICAjIFBjdHNBdFBjdGlsZV9OID0gQ250c0F0UGN0aWxlX04gLyBUcmF2RGlzdE1pSF9OdGlsZV9Sb3dzLAogICAgUGN0c0F0UGN0aWxlX0ggPSBDbnRzQXRQY3RpbGVfSCAvIFRyYXZEaXN0TWlIX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoIyBDdW1TdW1QQXRQX04gPSBjdW1zdW0oUGN0c0F0UGN0aWxlX04pLAogICAgICAgICBDdW1TdW1QQXRQX0ggPSBjdW1zdW0oUGN0c0F0UGN0aWxlX0gpCiAgICAgICAgKQoKIyBWaWV3KFRyYXZEaXN0TWlIX1BjdGlsZXMpClRyYXZEaXN0TWlIX1BjdGlsZXMKCmBgYAoKCkpvaW4gVHJhdkRpc3RNaUhfUGN0aWxlcywgVHJhdkRpc3RNaU5fUGN0aWxlcywgYW5kIFRyYXZEaXN0TWlfUGN0aWxlcy4KCn4xMSUgb2YgcmlkZXMgYXJlIHN0aWxsIHNob3dpbmcgYXMgbGVzcyB0aGFuIDAuMSBtaWxlcyBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLgpgYGB7cn0KCnJtKFRyYXZEaXN0TWlOX050aWxlX1Jvd3MsIFRyYXZEaXN0TWlIX050aWxlX1Jvd3MsIFRyYXZEaXN0TWlOX050aWxlLCBUcmF2RGlzdE1pSF9OdGlsZSkKCgojIFZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKQojIFZpZXcoVHJhdkRpc3RNaU5fUGN0aWxlcykKIyBWaWV3KFRyYXZEaXN0TWlIX1BjdGlsZXMpCgpUcmF2RGlzdE1pX1BjdGlsZXNfQWxsIDwtIGlubmVyX2pvaW4oeCA9IFRyYXZEaXN0TWlfUGN0aWxlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBUcmF2RGlzdE1pTl9QY3RpbGVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJQY3RSX1JvdW5kIiA9ICJQY3RSX1JvdW5kX04iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBpbm5lcl9qb2luKHkgPSBUcmF2RGlzdE1pSF9QY3RpbGVzLAogICAgICAgICAgICAgYnkgPSBjKCJQY3RSX1JvdW5kIiA9ICJQY3RSX1JvdW5kX0giKQogICAgICAgICAgICApICU+JSAKICBzZWxlY3QoUGN0Ul9Sb3VuZCwKICAgICAgICAgTWluVHJhdkRpc3RNaUF0UGN0aWxlLAogICAgICAgICBNaW5URE1pQXRQY3RpbGVfTiwKICAgICAgICAgTWluVERNaUF0UGN0aWxlX0gsCiAgICAgICAgIENudHNBdFBjdGlsZSwKICAgICAgICAgQ250c0F0UGN0aWxlX04sCiAgICAgICAgIENudHNBdFBjdGlsZV9ILAogICAgICAgICBQY3RzQXRQY3RpbGUsCiAgICAgICAgIFBjdHNBdFBjdGlsZV9OLAogICAgICAgICBQY3RzQXRQY3RpbGVfSCwKICAgICAgICAgQ3VtU3VtUEF0UCwKICAgICAgICAgQ3VtU3VtUEF0UF9OLAogICAgICAgICBDdW1TdW1QQXRQX0gKICAgICAgICAgKQoKIyBzdHIoVHJhdkRpc3RNaV9QY3RpbGVzX0FsbCkKCnJtKFRyYXZEaXN0TWlfUGN0aWxlcywgVHJhdkRpc3RNaU5fUGN0aWxlcyxUcmF2RGlzdE1pSF9QY3RpbGVzKQoKClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzX0FsbCkKVHJhdkRpc3RNaV9QY3RpbGVzX0FsbAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpXaHkgYXJlIHRoZXJlIHN0aWxsIHNvbWUgc21hbGwgb3IgbGFyZ2UgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyB2YWx1ZXMuCmBgYHtyfQoKIyBWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiMgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpCiMgICAgICAgICAgICApICU+JSAKIyAgICAgICAgc2VsZWN0KC1URF9NaV9xMiwKIyAgICAgICAgICAgICAgIC1URF9NaV9xOTgsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfcTUsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfcTk1LAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfcTUsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19xOTUsCiMgICAgICAgICAgICAgICAtVERfTWlfTWVhbiwKIyAgICAgICAgICAgICAgIC1URF9NaV9NZWFuX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfTWVhbiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWFuLAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfTWVhbl9GLAojICAgICAgICAgICAgICAgLVREX01pX01lZCwKIyAgICAgICAgICAgICAgIC1URF9NaV9NZWRfRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19NZWQsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfTWVkX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWQsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWRfRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9DbnQsCiMgICAgICAgICAgICAgICAtVERfTWlfQ250X0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfQ250LAojICAgICAgICAgICAgICAgLVREX01pX1NTX0NudF9GLAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfQ250LAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX3EyLAojICAgICAgICAgICAgICAgLVRUX1NlY19xOTgsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX3E1LAojICAgICAgICAgICAgICAgLVRUX1NlY19TU19xOTUsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfcTUsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfcTk1LAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWFuLAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19NZWFuLAojICAgICAgICAgICAgICAgLVRUX1NlY19TU0hHX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfTWVkLAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfTWVkLAojICAgICAgICAgICAgICAgLVRUX1NlY19TU19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19NZWQsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfTWVkX0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfQ250LAojICAgICAgICAgICAgICAgLVRUX1NlY19TU0hHX0NudF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX3EyLAojICAgICAgICAgICAgICAgLVRUX0hyX3E5OCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19xNSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19xOTUsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19xNSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX3E5NSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9NZWFuLAojICAgICAgICAgICAgICAgLVRUX0hyX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19NZWFuLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVFRfSHJfTWVkLAojICAgICAgICAgICAgICAgLVRUX0hyX01lZF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX01lZCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lZCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lZF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9DbnRfRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19DbnQsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19DbnQsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19DbnRfRgojICAgICAgICAgICAgICApICU+JSAKIyAgICAgICAgYXJyYW5nZShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSAlPiUgCiMgICAgICAgIGhlYWQoNTAwKQojICAgICApCgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyBvZiB0aGUgc21hbGxlc3QgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyB2YWx1ZXMuClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNDI0NDQwICYgUm93TnVtX09HIDw9IDE0MjQ0NjApIHwgIyAxNDI0NDUwICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA3NjMyOTIgJiBSb3dOdW1fT0cgPD0gNzYzMzEyKSB8ICMgNzYzMzAyICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjc5MDkzICYgUm93TnVtX09HIDw9IDE2NzkxMTMpIHwgIyAxNjc5MTAzICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyODYwOTE4ICYgUm93TnVtX09HIDw9IDI4NjA5MzgpICMgMjg2MDkyOCAgLS0gIGxvb2tzIGNvcnJlY3QKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdCgtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICApCiAgICApCgoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAhaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycykKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdCgtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKQogICAgICAgICAgICAgICkgJT4lCiAgICAgICBoZWFkKDUwMCkKICAgICkKCiMgZXhhbXBsZXMgb2YgdGhlIGxhcmdlc3QgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyB2YWx1ZXMuClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxMDkyMDAwICYgUm93TnVtX09HIDw9IDEwOTIwNTApIHwgIyAxMDkyMDMwICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjA5NDYwICYgUm93TnVtX09HIDw9IDE2MDk0ODApIHwgIyAxNjA5NDcwICAtLSBkaXJlY3Rpb24gY2hhbmdlIAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA1MDg5MDQgJiBSb3dOdW1fT0cgPD0gNTA4OTI0KSB8ICMgNTA4OTE0ICAtLSAgZGlyZWN0aW9uIGNoYW5nZSAmIG9yaWdpbmFsIFN0b3BJRCB3YXMgYmFkCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDI0NzYzNDUgJiBSb3dOdW1fT0cgPD0gMjQ3NjM2NSkgIyAyNDc2MzU1ICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9Ici4KClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKTogOTglIG9mIFRyYXZlbFRpbWVfSHIgYXJlIGJldHdlZW4gNyBzZWNvbmRzIGFuZCA0NjQgc2Vjb25kcyAofjggbWludXRlcykuCmBgYHtyfQoKVHJhdlRpbWVIcl9OdGlsZSA8LSBzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX0hyCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKCMgUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbFRpbWVfSHIsIDEwMCksCiAgICAgICAgICMgTWluUiA9IG1pbl9yYW5rKEFsbERheXNfTmV3VHJhdmVsRGlzdCRUcmF2ZWxUaW1lX0hyKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsVGltZV9IciksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgojIHN0cihUcmF2VGltZUhyX050aWxlKQoKVHJhdlRpbWVIcl9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdlRpbWVIcl9OdGlsZSkKCiMgVmlldyh0YWlsKFRyYXZUaW1lSHJfTnRpbGUsIDUwMCkpCgoKVHJhdlRpbWVIcl9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZUaW1lSHJfTnRpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblRyYXZUaW1lSHJBdFBjdGlsZSA9IG1pbihUcmF2ZWxUaW1lX0hyKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIFRyYXZUaW1lSHJfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSksCiAgICAgICAgIE1pblRyYXZUaW1lU2VjQXRQY3RpbGUgPSBNaW5UcmF2VGltZUhyQXRQY3RpbGUgKiAzNjAwCiAgICAgICAgKQoKcm0oVHJhdlRpbWVIcl9OdGlsZV9Sb3dzKQpybShUcmF2VGltZUhyX050aWxlKQpWaWV3KFRyYXZUaW1lSHJfUGN0aWxlcykKVHJhdlRpbWVIcl9QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfSHIuCgpIaXN0b2dyYW0gb2YgVHJhdmVsVGltZV9TZWMuCmBgYHtyfQoKVHJhdlRpbWVfU2VjX0hpc3REZW4gPC0gZ2dwbG90KGZpbHRlcihzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbFRpbWVfU2VjKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gVHJhdmVsVGltZV9TZWMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC4uZGVuc2l0eS4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgIyBzdGF0X2JpbihiaW53aWR0aCA9IDUsCiAgIyAgICAgICAgICBnZW9tID0gInRleHQiLAogICMgICAgICAgICAgc2l6ZSA9IDIuNSwKICAjICAgICAgICAgIHZqdXN0ID0gMS41LAogICMgICAgICAgICAgYWVzKGxhYmVsID0gZm9ybWF0KC4uY291bnQuLiwgYmlnLm1hcmsgPSAiLCIpCiAgIyAgICAgICAgICAgICApLAogICMgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMTgwKSwgeWxpbSA9IGMoMCwgMC4wMikKICAgICAgICAgICAgICAgICApICsKICAjICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gVHJhdmVsIFRpbWUiLAogICAgICAgeCA9ICJUcmF2ZWwgVGltZSAoc2VjKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiCiAgICAgICkKClRyYXZUaW1lX1NlY19IaXN0RGVuCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKVHJhdmVsVGltZV9TZWMgdmFsdWVzIGFyZSBOQS4KYGBge3J9CgpzdW1tYXJ5KEFsbERheXNfTmV3VHJhdmVsRGlzdCRUcmF2ZWxUaW1lX1NlYykKCgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcihpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgICMgVHJhdmVsVGltZSBwdXJwb3NlZnVsbHkgbm90IGNhbGN1bGF0ZWQgaGVyZQogICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlcyBvZiBUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgdGhhdCBhcmUgTkEuIFRoZXNlIGFyZSBOQSBiZWNhdXNlIHRoZSBFdmVudF9UaW1lICYgRGVwYXJ0dXJlX1RpbWUgcmVhZGluZ3MgYXJlIG5vdCBhY2N1cmF0ZSAoaS5lLiwgdGhlIHByZXZpb3VzIERlcGFydHVyZV9UaW1lIGlzIEJFRk9SRSBvciBFUVVBTCBUTyB0aGUgY3VycmVudCBFdmVudF9UaW1lKS4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDkwODA5ICYgUm93TnVtX09HIDw9IDkwODI5KSB8ICMgOTA4MTkKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gOTA4ODEgJiBSb3dOdW1fT0cgPD0gOTA5MDEpIHwgIyA5MDg5MQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyNTk3MDY2ICYgUm93TnVtX09HIDw9IDI1OTcwODYpIHwgIyAyNTk3MDc2CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDI2MTMzMDUgJiBSb3dOdW1fT0cgPD0gMjYxMzMyNSkgIyAyNjEzMzE1CiAgICAgICAgICAgKSAlPiUgCiAgICAgICBzZWxlY3QoLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKSkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgYXJlIGV4dHJlbWVseSBzbWFsbC4KYGBge3J9CgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcighaXMubmEoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoVHJhdmVsVGltZV9TZWMsCiAgICAgICAgICAgICAgIGRlc2MoU3BlZWRBdmdfTXBoX05ld0h2cnMpCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBzbWFsbCAoMSBzZWMpIGFuZCBTcGVlZEF2Z19NcGhfTmV3SHZycyBpcyBsYXJnZS4KVmlldyhzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBmaWx0ZXIoKFJvd051bV9PRyA+PSAyMjE3MzUzICYgUm93TnVtX09HIDw9IDIyMTczNzMpIHwgIyAyMjE3MzYzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDMwOTAzMjEgJiBSb3dOdW1fT0cgPD0gMzA5MDM0MSkgfCAjIDMwOTAzMzEKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gODA3NjQgJiBSb3dOdW1fT0cgPD0gODA3ODQpIHwgIyA4MDc3NAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMzg0MCAmIFJvd051bV9PRyA8PSAzMzg2MCkgIyAzMzg1MAogICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgYXJlIGV4dHJlbWVseSBsYXJnZS4KYGBge3J9CgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcighaXMubmEoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxUaW1lX1NlYyksCiAgICAgICAgICAgICAgIFNwZWVkQXZnX01waF9OZXdIdnJzCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBsYXJnZSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgc21hbGwuClZpZXcoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICkgJT4lIAogICAgICAgZmlsdGVyKChSb3dOdW1fT0cgPj0gMTAwNzcwMyAmIFJvd051bV9PRyA8PSAxMDA3NzIzKSB8ICMgMTAwNzcxMwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyMzczNTY0ICYgUm93TnVtX09HIDw9IDIzNzM1ODQpIHwgIyAyMzczNTc0CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NDM3OSAmIFJvd051bV9PRyA8PSA4NjQzOTkpIHwgIyA4NjQzODkKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjU3MDA2MCAmIFJvd051bV9PRyA8PSAyNTcwMDgwKSAjIDI1NzAwNzAKICAgICAgICAgICApCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKQXJlIGxhcmdlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyByZWxhdGVkIHRvIFJvdXRlQ2hhbmdlcz8gTG9va3MgbGlrZWx5LiBXaGVuIHRoZSBCdXMgaW52b2x2ZXMgYSBSb3V0ZSAiY2hhbmdlIiwgdGhlcmUgaXMgYWxtb3N0IHR3aWNlIGFzIGxpa2VseSB0byBiZSBhIGNhc2Ugb2YgYW4gb3V0bGllciBUcmF2ZWxUaW1lX1NlYyB2YWx1ZSAob24gdGhlIGhpZ2ggc2lkZSkuCmBgYHtyfQoKVFRMYXJnZVJ0ZUNobmcgPC0gc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFRUX091dCA9IGZhY3RvcihpZmVsc2UoVHJhdmVsVGltZV9TZWMgPiA0NjQsICAjIHRoaXMgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdXRsaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ybWFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKIyBzdHIoVFRMYXJnZVJ0ZUNobmcpCgoKVFRMYXJnZVJ0ZUNobmdfQ250cyA8LSBncm91cF9ieShUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdGVDaGFuZ2UyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX091dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKENudHMgPSBuKCkKICAgICAgICAgICApCgpUVExhcmdlUnRlQ2huZ19TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoVFRMYXJnZVJ0ZUNobmdfQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX091dCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENudHMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lCiAgc2VsZWN0KC1SdGVDaGFuZ2UyKQoKcm93Lm5hbWVzKFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkgPC0gYygiQ2hhbmdlIiwgIlNhbWUiKQojIHN0cihUVExhcmdlUnRlQ2huZ19TcHJlYWQpCgoKIyBXaGVuIHRoZSBCdXMgaW52b2x2ZXMgYSBSb3V0ZSAiY2hhbmdlIiwgdGhlcmUgaXMgYWxtb3N0IHR3aWNlIGFzIGxpa2VseSB0byBiZSBhIGNhc2Ugb2YgYW4gb3V0bGllciBUcmF2ZWxUaW1lX1NlYyB2YWx1ZS4KVFRMYXJnZVJ0ZUNobmdfU3ByZWFkCnByb3AudGFibGUoYXMudGFibGUoYXMubWF0cml4KFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVFRMYXJnZVJ0ZUNobmdfU3ByZWFkKQogICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAyCiAgICAgICAgICApCgojIHJtKFRUTGFyZ2VSdGVDaG5nLCBUVExhcmdlUnRlQ2huZ19TcHJlYWQpCiAgICAgICAgIApgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKQXJlIGxhcmdlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyByZWxhdGVkIHRvIFJvdXRlQ2hhbmdlcz8gTG9va3MgbGlrZWx5LgpgYGB7cn0KClZpZXcoZmlsdGVyKFRUTGFyZ2VSdGVDaG5nLAogICAgICAgICAgICAhaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICBSdGVDaGFuZ2UyID09ICJTYW1lIgogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbFRpbWVfU2VjKSwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoX05ld0h2cnMKICAgICAgICAgICAgICApICU+JQogICAgICAgaGVhZCg1MDApCiAgICApCgoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBsYXJnZSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgc21hbGwuClZpZXcoZmlsdGVyKFRUTGFyZ2VSdGVDaG5nLAogICAgICAgICAgICAoUm93TnVtX09HID49IDIyNTAyOTAgJiBSb3dOdW1fT0cgPD0gMjI1MDMxMCkgfCAjIDIyNTAzMDAKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NzcxNyAmIFJvd051bV9PRyA8PSA4Njc3MzcpIHwgIyA4Njc3MjcKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NDM3OSAmIFJvd051bV9PRyA8PSA4NjQzOTkpIHwgIyA4NjQzODkKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDgwODM5NSAmIFJvd051bV9PRyA8PSA4MDg0MTUpICMgODA4NDA1CiAgICAgICAgICAgKQogICAgKQoKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpJZiBUcmF2ZWxUaW1lX1NlYyBpcyBiZWxvdyB0aGUgNXRoIHBlcmNlbnRpbGUgZm9yIHRoYXQgU3RhcnRTdG9wX0lELCBvciBpZiBUcmF2ZWxUaW1lX1NlYyBpcyBhYm92ZSB0aGUgOTV0aCBwZXJjZW50aWxlIGZvciB0aGF0IFN0YXJ0U3RvcF9JRCwgIGNvbnNpZGVyIHRoaXMgYW4gb3V0bGllci4gIEluIHRoaXMgY2FzZSwgcmVwbGFjZSB0aGUgdmFsdWUgd2l0aCB0aGUgbWVhbiBmb3IgdGhhdCBTdGFydFN0b3BfSUQgYW5kIEhvdXJHcm91cCAoVFRfU2VjX1NTSEdfTWVhbl9GKSwgb3IgaWYgdGhlcmUgYXJlIG5vdCBlbm91Z2ggdmFsdWVzIGF0IHRoZSBIb3VyR3JvdXAgbGV2ZWwsIHJlcGxhY2UgaXQgd2l0aCB0aGUgbWVhbiBmb3IgdGhhdCBTdGFydFN0b3BfSUQuCmBgYHtyfQoKcm0oVFRMYXJnZVJ0ZUNobmcsIFRUTGFyZ2VSdGVDaG5nX0NudHMsIFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkKCgpOZXdUcmF2VGltZSA8LSBtdXRhdGUoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX05ldyA9IGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxUaW1lX1NlYyA8IFRUX1NlY19TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IFRUX1NlY19TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250ID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnQgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnRlQ2hhbmdlMiA9PSAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCA9IAogICAgICAgICAgIGZhY3RvcihpZmVsc2UoIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxUaW1lX1NlYyA8IFRUX1NlY19TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX0NudF9GID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlRUX1NlY19TU0hHX01lYW5fRiIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IFRUX1NlY19TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJUVF9TZWNfU1NfTWVhbl9GIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250ID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlRUX1NlY19TU19NZWFuIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnQgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ0ZUNoYW5nZTIgPT0gIkNoYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICJUcmF2ZWxUaW1lX1NlYyIKICAgICAgICAgICAgICAgICAgICAgICAgKSkpKQogICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICBUVF9Icl9OZXcgPSBUVF9TZWNfTmV3IC8gKDYwICogNjApCiAgICAgICAgICAgKQoKCmRpbShBbGxEYXlzX05ld1RyYXZlbERpc3QpCmRpbShOZXdUcmF2VGltZSkKcm0oQWxsRGF5c19OZXdUcmF2ZWxEaXN0KQoKc3VtbWFyeShzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgKQogICApCgpzdHIoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgIFRyYXZlbFRpbWVfU2VjLAogICAgICAgICAgIFRUX1NlY19OZXcsCiAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCwKICAgICAgICAgICBUVF9Icl9OZXcKICAgICAgICAgICkKICAgKQoKCnN1bW1hcnkoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYywKICAgICAgICAgICAgICAgVFRfU2VjX05ldywKICAgICAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCwKICAgICAgICAgICAgICAgVFRfSHJfTmV3CiAgICAgICAgICAgICAgKQogICAgICAgKQoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBCb3ggcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IEhvdXJHcm91cCkuCmBgYHtyfQoKVmlldyhoZWFkKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICAgICApCiAgICAgICAgICkKICAgICkKClgyIDwtIHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFJvdXRlID09ICJYMiIpCgpzdHIoWDIpCgpWaWV3KGhlYWQoYXJyYW5nZShYMiwKICAgICAgICAgICAgICAgICAgQnVzX0lELAogICAgICAgICAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpYMl9CeVN0b3AgPC0gZ3JvdXBfYnkoWDIsCiAgICAgICAgICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4KICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lKSAlPiUgCiAgbXV0YXRlKEV2ZW50X1RpbWVfTDEgPSBsYWcoRXZlbnRfVGltZSksCiAgICAgICAgIFRpbWVUb0V2ZW50X1NlYyA9IGFzLm51bWVyaWMoRXZlbnRfVGltZSAtIEV2ZW50X1RpbWVfTDEpLAogICAgICAgICBUaW1lVG9FdmVudF9NaW4gPSBUaW1lVG9FdmVudF9TZWMgLyA2MAogICAgICAgICkKClZpZXcoaGVhZChYMl9CeVN0b3AsIDUwMCkpCgoKIyBDb3VudF9WYWx1ZXMgaXMgbmVlZGVkIHRvIGRpc3BsYXkgdGhlIG1lZGlhbnMgb24gdGhlIGJveCBwbG90cwpDb3VudF9WYWx1ZXMgPC0gZGRwbHkoYXMuZGF0YS5mcmFtZShYMl9CeVN0b3ApLAogICAgICAgICAgICAgICAgICAgICAgLihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlLAogICAgICAgICAgICAgICAgICAgICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKFRpbWVUb0V2ZW50X01pbiwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICApCgpUaW1lQnR3RXZlbnRzX1gyX0JveFBsb3QgPC0gZ2dwbG90KHNlbGVjdChhcy5kYXRhLmZyYW1lKFgyX0J5U3RvcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91cj0icmVkIiwgbm90Y2g9VFJVRSwgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudF9WYWx1ZXMsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMTIwKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiSG93IE9mdGVuIGFuIFgyIEFycml2ZXMgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHggPSAiSG91ciBHcm91cCIsCiAgICAgICB5ID0gIlRpbWUgQmV0d2VlbiBCdXNzZXMgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfQm94UGxvdAoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBWaW9saW4gcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IEhvdXIgR3JvdXApLgpgYGB7cn0KClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdCA8LSBnZ3Bsb3Qoc2VsZWN0KGFzLmRhdGEuZnJhbWUoWDJfQnlTdG9wKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYygwLjI1LCAwLjUsIDAuNzUpLAogICAgICAgICAgICAgIHRyaW0gPSBUUlVFLAogICAgICAgICAgICAgIHNjYWxlID0gImNvdW50IiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsCiAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBOQSwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IFRSVUUKICAgICAgICAgICAgICkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRfVmFsdWVzLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgODApCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgT2Z0ZW4gYW4gWDIgQXJyaXZlcyBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgeCA9ICJIb3VyIEdyb3VwIiwKICAgICAgIHkgPSAiVGltZSBCZXR3ZWVuIEJ1c3NlcyAobWluKSIKICAgICAgKQoKVGltZUJ0d0V2ZW50c19YMl9WaW9saW5QbG90CgpgYGAKCgpUZXN0IGludmVzdGlnYXRpb24gb2YganVzdCB0aGUgWDIgUm91dGUuIEJveCBwbG90cyBmb3IgdGltZSBiZXR3ZWVuIGJ1cyBhcnJpdmFscyAoYnkgWmlwIENvZGUpLgpgYGB7cn0KCiMgQ291bnRfVmFsdWVzIGlzIG5lZWRlZCB0byBkaXNwbGF5IHRoZSBtZWRpYW5zIG9uIHRoZSBib3ggcGxvdHMKQ291bnRfVmFsdWVzX3ogPC0gZGRwbHkoYXMuZGF0YS5mcmFtZShYMl9CeVN0b3ApLAogICAgICAgICAgICAgICAgICAgICAgICAuKFN0b3BfWmlwKSwKICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlLAogICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9Db3VudHMgPSBtZWRpYW4oVGltZVRvRXZlbnRfTWluLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgICAgICAgICAgICAgKQoKVGltZUJ0d0V2ZW50c19YMl9Cb3hQbG90X3ogPC0gZ2dwbG90KHNlbGVjdChhcy5kYXRhLmZyYW1lKFgyX0J5U3RvcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoU3RvcF9aaXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXI9InJlZCIsIG5vdGNoPVRSVUUsIG5hLnJtID0gVFJVRSkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRfVmFsdWVzX3osCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMTAwKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiSG93IE9mdGVuIGFuIFgyIEFycml2ZXMgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHggPSAiWmlwIENvZGUgb2YgRGVzdGluYXRpb24iLAogICAgICAgeSA9ICJUaW1lIEJldHdlZW4gQnVzc2VzIChtaW4pIgogICAgICApCgpUaW1lQnR3RXZlbnRzX1gyX0JveFBsb3RfegoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBWaW9saW4gcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IFppcCBDb2RlKS4KYGBge3J9CgpUaW1lQnR3RXZlbnRzX1gyX1Zpb2xpblBsb3RfeiA8LSBnZ3Bsb3Qoc2VsZWN0KGFzLmRhdGEuZnJhbWUoWDJfQnlTdG9wKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lVG9FdmVudF9NaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoU3RvcF9aaXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoMC4yNSwgMC41LCAwLjc1KSwKICAgICAgICAgICAgICB0cmltID0gVFJVRSwKICAgICAgICAgICAgICBzY2FsZSA9ICJjb3VudCIsCiAgICAgICAgICAgICAgbmEucm0gPSBUUlVFLAogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gTkEsCiAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBUUlVFCiAgICAgICAgICAgICApICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50X1ZhbHVlc196LAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNjApCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgT2Z0ZW4gYW4gWDIgQXJyaXZlcyBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgeCA9ICJaaXAgQ29kZSBvZiBEZXN0aW5hdGlvbiIsCiAgICAgICB5ID0gIlRpbWUgQmV0d2VlbiBCdXNzZXMgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdF96CgpgYGAKCgpXYWl0aW5nIHRpbWUgYW5hbHlzZXMuCgpNdW5naW5nIGFuZCBzYW1wbGluZyBkYXRhIHRvIGdvIGZyb20gdGltZSBiZXRlZW4gYnVzZXMgdG8gImF2ZXJhZ2UiIHdhaXRpbmcgdGltZS4KCkZpcnN0LCBnZXQgdGhlIG1heCBhbmQgbWluIHRpbWVzIG9mIGJ1cyBzdG9wcyAoZWFjaCBkYXksIGFuZCBmb3IgZWFjaCByb3V0ZSkuCmBgYHtyfQoKcm0oWDIsIFgyX0J5U3RvcCwgWDJfTG9uZywgWDJfUGN0KQoKClJvdXRlTWluTWF4IDwtIGdyb3VwX2J5KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlCiAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKE1pblRpbWUgPSBtaW4oRXZlbnRfVGltZSksCiAgICAgICAgICAgIE1heFRpbWUgPSBtYXgoRXZlbnRfVGltZSkKICAgICAgICAgICApCgpzdHIoUm91dGVNaW5NYXgpClZpZXcoUm91dGVNaW5NYXgpCgpgYGAKCgpXYWl0aW5nIHRpbWUgYW5hbHlzZXMuCgpNdW5naW5nIGFuZCBzYW1wbGluZyBkYXRhIHRvIGdvIGZyb20gdGltZSBiZXRlZW4gYnVzZXMgdG8gImF2ZXJhZ2UiIHdhaXRpbmcgdGltZS4KCihQdWxscyBoZXJlIGFyZSBkb25lIGJ5IGRheSwgYXMgdGhlIGRhdGEgYXJlIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlLikKYGBge3J9CgojIFZpZXcoaGVhZChOZXdUcmF2VGltZSwgNTAwKSkKCiMgRm9yIGVhY2ggcmVjb3JkLCBjcmVhdGUgYSByYW5kb20gZGF0ZXRpbWUgYmV0d2VlbiB0aGUgZmlyc3QgYW5kIGxhc3Qgc3RvcCBmb3IgdGhhdCBidXMgcm91dGUgKG9uIHRoYXQgZGF5KS4KZm9yKGkgaW4gMzo3KXsKCnNldC5zZWVkKDEyMzQ1Njc4OSkKU2FtcCA8LSBzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAgIFJvd051bV9PRywKICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICMgUm91dGVHcm91cCwKICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJFdmVudCIpCiAgICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKEV2ZW50X1RpbWVfRGF0ZSA9PSBpKSAlPiUgICMgbmVlZGVkIHRvIGRvIHRoaXMgZWFjaCBkYXkgKDMtNykgYmVjYXVzZSB0aGUgY29tcGxldGUgZmlsZSB3YXMgdG9vIGxhcmdlIHRvIGRvIGF0IG9uY2UKICBsZWZ0X2pvaW4oUm91dGVNaW5NYXgsCiAgICAgICAgICAgIGJ5ID0gYygiUm91dGUiID0gIlJvdXRlIiwKICAgICAgICAgICAgICAgICAgICJFdmVudF9UaW1lX0RhdGUiID0gIkV2ZW50X1RpbWVfRGF0ZSIKICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICkgJT4lIAogIG11dGF0ZShTYW1wVGltZSA9IGFzX2RhdGV0aW1lKHJ1bmlmKG5yb3coLiksICMyMDAwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gTWluVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXggPSBNYXhUaW1lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShSb3V0ZSwKICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgKSAKCiMgc3RyKFNhbXApCiMgVmlldyhoZWFkKFNhbXAsIDUwMCkpCiMgCiMgVmlldygKIyBncm91cF9ieShTYW1wLAojICAgICAgICAgIFJvd051bV9PRwojICAgICAgICAgKSAlPiUKIyAgIHN1bW1hcmlzZShDbnRfTnVtID0gbigpLAojICAgICAgICAgICAgIENudF9QY3QgPSAxMDAgKiBDbnRfTnVtIC8gbnJvdyhTYW1wKQojICAgICAgICAgICAgKSAlPiUKIyAgIGFycmFuZ2UoZGVzYyhDbnRfTnVtKSkKIyApCgoKIyBGb3IgZWFjaCBSb3V0ZSBhbmQgU3RvcElEIGNvbWJpbmF0aW9uLCBnZXQgYWxsIHRoZSBFdmVudF9UaW1lIHZhbHVlcyB0aGF0IGFyZSBhZnRlciB0aGUgU2FtcFRpbWUgdmFsdWUuCiMgZXN0aW1hdGluZyBhcHByb3ggMmhycyBvZiBydW50aW1lIGZvciBhbGwgMi44bSByZWNvcmRzClRlc3RpbmdfQSA8LSBzcWxkZigiICAgU2VsZWN0ICAgICAgICAgICAgICAgdDEuKgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0Mi5FdmVudF9UaW1lICAgICAgICAgICAgIGFzIE5leHRCdXMKICAgICAgICAgICAgICAgICAgICAgICAgRnJvbSAgICAgICAgICAgICAgICAgU2FtcCAgICAgICAgICAgICAgICAgICAgICBhcyB0MQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIElubmVyIEpvaW4gICAgICBTYW1wICAgICAgICAgICAgICAgICAgICAgIGFzIHQyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT24gICAgICAgICAgICAgIHQxLlJvdXRlID0gdDIuUm91dGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbmQgICAgICAgICAgICAgdDEuU3RvcElEX0NsZWFuID0gdDIuU3RvcElEX0NsZWFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQW5kICAgICAgICAgICAgIHQyLkV2ZW50X1RpbWUgPiB0MS5TYW1wVGltZQogICAgICAgICAgICAgICAgICAgICAgICBPcmRlciBCeSAgICAgICAgICAgICB0MS5Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0MS5TdG9wSURfQ2xlYW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDEuRXZlbnRfVGltZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0Mi5FdmVudF9UaW1lCiAgICAgICAgICAgICAgICAgICIKICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoTkIgPSBhc19kYXRldGltZShOZXh0QnVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiCiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKIyBzdHIoVGVzdGluZ19BKQojIFZpZXcoaGVhZChUZXN0aW5nX0EsIDUwMCkpCiMgVmlldyhoZWFkKFNhbXAsIDUwMCkpCgoKIyBGaWx0ZXIgdGhlIGRhdGFmcmFtZSB0byBvbmx5IGluY2x1ZGUgdGhlIGJ1cyBhcnJpdmFsIGF0IFN0b3BJRCB0aGF0IGlzIHRoZSBuZXh0IHRvIGNvbWUgYWZ0ZXIgdGhlIFNhbXBUaW1lLgojIGVzdGltYXRpbmcgYXBwcm94IDIwbWluIG9mIHJ1bnRpbWUgZm9yIGFsbCAyLjhtIHJlY29yZHMKVGVzdGluZyA8LSBzZWxlY3QoVGVzdGluZ19BLAogICAgICAgICAgICAgICAgICAtTmV4dEJ1cwogICAgICAgICAgICAgICAgICkgJT4lIAogIGdyb3VwX2J5KFJvd051bV9PRykgJT4lIAogIGZpbHRlcihOQiA9PSBtaW4oTkIpCiAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShSb3V0ZSwKICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFdhaXRUaW1lX01pbiA9IGFzLm51bWVyaWMoTkIgLSBTYW1wVGltZSksCiAgICAgICAgIFdhaXRUaW1lX1NlYyA9IFdhaXRUaW1lX01pbiAqIDYwLAogICAgICAgICBXYWl0VGltZV9TZWMyID0gTkIgLSBTYW1wVGltZSwKICAgICAgICAgV2FpdFRpbWVfTWluMiA9IFdhaXRUaW1lX1NlYzIgLyA2MAogICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKYXNzaWduKHBhc3RlMCgiVGVzdGluZ18iLCBpKSwKICAgICAgIFRlc3RpbmcKICAgICAgKQoKcm0oU2FtcCxUZXN0aW5nX0EsIFRlc3RpbmcpCnN0cihnZXQocGFzdGUwKCJUZXN0aW5nXyIsIGkpKSkKVmlldyhnZXQocGFzdGUwKCJUZXN0aW5nXyIsIGkpKSkKfQoKCiMgQmluZCBhbGwgdGhlIGluZGl2aWR1YWwgZGF0YWZyYW1lcyB0b2dldGhlci4KV2FpdERhdGFfRGF5UHVsbCA8LSBiaW5kX3Jvd3MoVGVzdGluZ18zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nXzQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRlc3RpbmdfNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGVzdGluZ182LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nXzcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoV2FpdFRpbWVfU2VjMyA9IE5CIC0gU2FtcFRpbWUsCiAgICAgICAgIFdhaXRUaW1lX01pbjMgPSBXYWl0VGltZV9TZWMzIC8gNjAKICAgICAgICApICU+JSAKICBhcnJhbmdlKFJvdXRlLAogICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgRXZlbnRfVGltZQogICAgICAgICApCgpybShUZXN0aW5nXzMsIFRlc3RpbmdfNCwgVGVzdGluZ181LCBUZXN0aW5nXzYsIFRlc3RpbmdfNykKc3RyKFdhaXREYXRhX0RheVB1bGwpClZpZXcoaGVhZChXYWl0RGF0YV9EYXlQdWxsLCA1MDApKQpWaWV3KHRhaWwoV2FpdERhdGFfRGF5UHVsbCwgNTAwKSkKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKQmFzaWMgaW52ZXN0aWdhdGlvbiBvZiBhbnkgbWlzc2luZyByb3dzIGZyb20gZGF0YSBwdWxsZWQgYnkgZGF5LgpgYGB7cn0KCkRpc3RpbmN0Um93TnVtX09HIDwtIGRpc3RpbmN0KHNlbGVjdChXYWl0RGF0YV9EYXlQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpzdHIoRGlzdGluY3RSb3dOdW1fT0cpCgojIFZpZXcoCiMgYW50aV9qb2luKFNhbXAsCiMgICAgICAgICAgIERpc3RpbmN0Um93TnVtX09HLAojICAgICAgICAgICBieSA9IGMoIlJvd051bV9PRyIgPSAiUm93TnVtX09HIikKIyAgICAgICAgICApCiMgKQoKCiMgVGhlIHNhbXAgdGltZSBpcyBBRlRFUiB0aGUgbGFzdCBidXMgcGFzc2VkIHRoYXQgU3RvcElEX0NsZWFuCiMgVmlldyhmaWx0ZXIoU2FtcCwKIyAgICAgICAgICAgICBFdmVudF9UaW1lID4gIjIwMTYtMTAtMDcgMTk6NDg6NDEiICYKIyAgICAgICAgICAgICAgIFJvdXRlID09ICJYMiIgJgojICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuID09IDEwMDM3NzQKIyAgICAgICAgICAgICkKIyAgICAgKQoKIyBOZXh0IEJ1cyAoTkIpIGNhbiBiZSBvbiB0aGUgbmV4dCBtb3JuaW5nCiMgVmlldyhmaWx0ZXIoVGVzdGluZzcsCiMgICAgICAgICAgICAgU2FtcFRpbWUgPiAiMjAxNi0xMC0wNiAyMzo1ODowMCIgJgojICAgICAgICAgICAgICAgU2FtcFRpbWUgPCAiMjAxNi0xMC0wNiAyMzo1OTo1OSIpCiMgICAgICkKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKKFB1bGxzIGhlcmUgYXJlIGRvbmUgYnkgZ3JvdXBpbmdzIG9mIGJ1cyByb3V0ZXMsIGFzIHRoZSBkYXRhIGFyZSB0b28gbGFyZ2UgdG8gZG8gYXQgb25jZS4pCgpGaXJzdCwgd2UgbmVlZCB0byBmaW5kIHRoZSBtb3N0IGNvbW1vbiBidXMgcm91dGVzLgpgYGB7cn0KCnJtKERpc3RpbmN0Um93TnVtX09HKQoKCiMgVmlldyhoZWFkKE5ld1RyYXZUaW1lLCA1MDApKQoKc2V0LnNlZWQoMTIzNDU2Nzg5KQpCdXNHcm91cHMgPC0gZ3JvdXBfYnkoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAgICAgICAgICBSb3V0ZQogICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoQ250X051bSA9IG4oKSwKICAgICAgICAgICAgQ250X1BjdCA9IENudF9OdW0gLyBucm93KE5ld1RyYXZUaW1lKQogICAgICAgICAgICkgJT4lIAogIGFycmFuZ2UoZGVzYyhDbnRfTnVtKQogICAgICAgICApICU+JSAKICBtdXRhdGUoUm93TnVtID0gcm93X251bWJlcigpLAogICAgICAgICBSYW5kTnVtID0gcnVuaWYobiA9IDI2OCksCiAgICAgICAgIFJvdXRlR3JvdXAgPSBpZmVsc2UoUmFuZE51bSA8PSAwLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMSwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShSYW5kTnVtIDw9IDAuNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFJhbmROdW0gPD0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUmFuZE51bSA8PSAwLjgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICA1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkpCiAgICAgICAgKQoKc3RyKEJ1c0dyb3VwcykKVmlldyhCdXNHcm91cHMpCnN1bW1hcnkoQnVzR3JvdXBzKQoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgooUHVsbHMgaGVyZSBhcmUgZG9uZSBieSBncm91cGluZ3Mgb2YgYnVzIHJvdXRlcywgYXMgdGhlIGRhdGEgYXJlIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlLikKYGBge3J9CgojIFZpZXcoaGVhZChOZXdUcmF2VGltZSwgNTAwKSkKCiMgRm9yIGVhY2ggcmVjb3JkLCBjcmVhdGUgYSByYW5kb20gZGF0ZXRpbWUgYmV0d2VlbiB0aGUgZmlyc3QgYW5kIGxhc3Qgc3RvcCBmb3IgdGhhdCBidXMgcm91dGUgKG9uIHRoYXQgZGF5KS4KZm9yKGkgaW4gMTo1KXsKICAKc2V0LnNlZWQoMTIzNDU2Nzg5KQpTYW1wIDwtIGxlZnRfam9pbihOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgICAgQnVzR3JvdXBzLAogICAgICAgICAgICAgICAgICBieSA9IGMoIlJvdXRlIiA9ICJSb3V0ZSIpCiAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChSb3dOdW1fT0csCiAgICAgICAgIFJvdXRlLAogICAgICAgICBSb3V0ZUdyb3VwLAogICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgc3RhcnRzX3dpdGgoIkV2ZW50IikKICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGVHcm91cCA9PSBpKSAlPiUgICMgbmVlZGVkIHRvIGRvIHRoaXMgZWFjaCBSb3V0ZUdyb3VwICgxLTUpIGJlY2F1c2UgdGhlIGNvbXBsZXRlIGZpbGUgd2FzIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlCiAgbGVmdF9qb2luKFJvdXRlTWluTWF4LAogICAgICAgICAgICBieSA9IGMoIlJvdXRlIiA9ICJSb3V0ZSIsCiAgICAgICAgICAgICAgICAgICAiRXZlbnRfVGltZV9EYXRlIiA9ICJFdmVudF9UaW1lX0RhdGUiCiAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICApICU+JSAKICBtdXRhdGUoU2FtcFRpbWUgPSBhc19kYXRldGltZShydW5pZihucm93KC4pLCAjMjAwMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbiA9IE1pblRpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4ID0gTWF4VGltZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eiA9ICJBbWVyaWNhL05ld19Zb3JrIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkgCgojIHN0cihTYW1wKQojIFZpZXcoaGVhZChTYW1wLCA1MDApKQojIAojIFZpZXcoCiMgZ3JvdXBfYnkoU2FtcCwKIyAgICAgICAgICBSb3dOdW1fT0cKIyAgICAgICAgICkgJT4lCiMgICBzdW1tYXJpc2UoQ250X051bSA9IG4oKSwKIyAgICAgICAgICAgICBDbnRfUGN0ID0gMTAwICogQ250X051bSAvIG5yb3coU2FtcCkKIyAgICAgICAgICAgICkgJT4lCiMgICBhcnJhbmdlKGRlc2MoQ250X051bSkpCiMgKQoKCiMgRm9yIGVhY2ggUm91dGUgYW5kIFN0b3BJRCBjb21iaW5hdGlvbiwgZ2V0IGFsbCB0aGUgRXZlbnRfVGltZSB2YWx1ZXMgdGhhdCBhcmUgYWZ0ZXIgdGhlIFNhbXBUaW1lIHZhbHVlLgojIGVzdGltYXRpbmcgYXBwcm94IDJocnMgb2YgcnVudGltZSBmb3IgYWxsIDIuOG0gcmVjb3JkcwpUZXN0aW5nX0EgPC0gc3FsZGYoIiAgIFNlbGVjdCAgICAgICAgICAgICAgIHQxLioKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDIuRXZlbnRfVGltZSAgICAgICAgICAgICBhcyBOZXh0QnVzCiAgICAgICAgICAgICAgICAgICAgICAgIEZyb20gICAgICAgICAgICAgICAgIFNhbXAgICAgICAgICAgICAgICAgICAgICAgYXMgdDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbm5lciBKb2luICAgICAgU2FtcCAgICAgICAgICAgICAgICAgICAgICBhcyB0MgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9uICAgICAgICAgICAgICB0MS5Sb3V0ZSA9IHQyLlJvdXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQW5kICAgICAgICAgICAgIHQxLlN0b3BJRF9DbGVhbiA9IHQyLlN0b3BJRF9DbGVhbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFuZCAgICAgICAgICAgICB0Mi5FdmVudF9UaW1lID4gdDEuU2FtcFRpbWUKICAgICAgICAgICAgICAgICAgICAgICAgT3JkZXIgQnkgICAgICAgICAgICAgdDEuUm91dGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDEuU3RvcElEX0NsZWFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHQxLkV2ZW50X1RpbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDIuRXZlbnRfVGltZQogICAgICAgICAgICAgICAgICAiCiAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKE5CID0gYXNfZGF0ZXRpbWUoTmV4dEJ1cywKICAgICAgICAgICAgICAgICAgICAgICAgICB0eiA9ICJBbWVyaWNhL05ld19Zb3JrIgogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkKCiMgc3RyKFRlc3RpbmdfQSkKIyBWaWV3KGhlYWQoVGVzdGluZ19BLCA1MDApKQojIFZpZXcoaGVhZChTYW1wLCA1MDApKQoKCiMgRmlsdGVyIHRoZSBkYXRhZnJhbWUgdG8gb25seSBpbmNsdWRlIHRoZSBidXMgYXJyaXZhbCBhdCBTdG9wSUQgdGhhdCBpcyB0aGUgbmV4dCB0byBjb21lIGFmdGVyIHRoZSBTYW1wVGltZS4KIyBlc3RpbWF0aW5nIGFwcHJveCAyMG1pbiBvZiBydW50aW1lIGZvciBhbGwgMi44bSByZWNvcmRzClRlc3RpbmcgPC0gc2VsZWN0KFRlc3RpbmdfQSwKICAgICAgICAgICAgICAgICAgLU5leHRCdXMKICAgICAgICAgICAgICAgICApICU+JSAKICBncm91cF9ieShSb3dOdW1fT0cpICU+JSAKICBmaWx0ZXIoTkIgPT0gbWluKE5CKQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkgJT4lIAogIG11dGF0ZShXYWl0VGltZV9NaW4gPSBhcy5udW1lcmljKE5CIC0gU2FtcFRpbWUpLAogICAgICAgICBXYWl0VGltZV9TZWMgPSBXYWl0VGltZV9NaW4gKiA2MAogICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKYXNzaWduKHBhc3RlMCgiVGVzdGluZyIsIGkpLAogICAgICAgVGVzdGluZwogICAgICApCgpybShTYW1wLFRlc3RpbmdfQSwgVGVzdGluZykKc3RyKGdldChwYXN0ZTAoIlRlc3RpbmciLCBpKSkpClZpZXcoZ2V0KHBhc3RlMCgiVGVzdGluZyIsIGkpKSkKfQoKCiMgQmluZCBhbGwgdGhlIGluZGl2aWR1YWwgZGF0YWZyYW1lcyB0b2dldGhlci4KV2FpdERhdGFfUm91dGVQdWxsIDwtIGJpbmRfcm93cyhUZXN0aW5nMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nNQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShXYWl0VGltZV9TZWMyID0gTkIgLSBTYW1wVGltZSwKICAgICAgICAgV2FpdFRpbWVfTWluMiA9IFdhaXRUaW1lX1NlYzIgLyA2MAogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkKCnJtKEJ1c0dyb3VwcywgaSwgVGVzdGluZzMsIFRlc3Rpbmc0LCBUZXN0aW5nNSwgVGVzdGluZzYsIFRlc3Rpbmc3KQpzdHIoV2FpdERhdGFfUm91dGVQdWxsKQpWaWV3KGhlYWQoV2FpdERhdGFfUm91dGVQdWxsLCA1MDApKQpWaWV3KHRhaWwoV2FpdERhdGFfUm91dGVQdWxsLCA1MDApKQoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgpDb21wYXJlIFdhaXREYXRhIHB1bGxlZCBieSBkYXkgYW5kIHB1bGxlZCBieSByb3V0ZS4KYGBge3J9CgpkaW0oV2FpdERhdGFfUm91dGVQdWxsKQpkaW0oV2FpdERhdGFfRGF5UHVsbCkKbnJvdyhXYWl0RGF0YV9Sb3V0ZVB1bGwpIC0gbnJvdyhXYWl0RGF0YV9EYXlQdWxsKQoKV2FpdERhdGFfRGlmZiA8LSBhbnRpX2pvaW4oV2FpdERhdGFfUm91dGVQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0RGF0YV9EYXlQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlJvd051bV9PRyIgPSAiUm93TnVtX09HIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc2VsZWN0KC1XYWl0VGltZV9NaW4sCiAgICAgICAgIC1XYWl0VGltZV9TZWMKICAgICAgICApCgpzdHIoV2FpdERhdGFfRGlmZikKVmlldyhoZWFkKFdhaXREYXRhX0RpZmYsIDUwMCkpCgpWaWV3KGZpbHRlcihXYWl0RGF0YV9Sb3V0ZVB1bGwsCiAgICAgICAgICAgIFJvdXRlID09ICJaOCIgJgogICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiA9PSAyMDA1NDY1CiAgICAgICAgICAgICMgUm93TnVtX09HID0gMjkwMjc2MAogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA3IDE5OjUxOjQ3CiAgICAgICAgICAgKQogICAgKQoKVmlldyhncm91cF9ieShXYWl0RGF0YV9EaWZmLAogICAgICAgICAgICAgIFJvdXRlCiAgICAgICAgICAgICApICU+JSAKICAgICAgIHN1bW1hcmlzZShDbnRfTnVtID0gbigpLAogICAgICAgICAgICAgICAgIENudF9QY3QgPSBDbnRfTnVtIC8gbnJvdyhXYWl0RGF0YV9EaWZmKQogICAgICAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoQ250X051bSkKICAgICAgICAgICAgICApCiAgICApCgpWaWV3KGZpbHRlcihXYWl0RGF0YV9EaWZmLAogICAgICAgICAgICBSb3V0ZSA9PSAiUzEiCiAgICAgICAgICAgKQogICAgKQoKVmlldyhmaWx0ZXIoV2FpdERhdGFfUm91dGVQdWxsLAogICAgICAgICAgICBSb3V0ZSA9PSAiUzEiICYKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMzEzMgogICAgICAgICAgICAjIFJvd051bV9PRyA9IDExNTE3NzAKICAgICAgICAgICAgIyBFdmVudF9UaW1lID0gMjAxNi0xMC0wNyAwOTowNzoxMgogICAgICAgICAgICkKICAgICkKCiMgQ2FuJ3QgdGVsbCB3aHkgdGhlIHB1bGwgYnkgZGF5IGhhcyBsZXNzIHJlY29yZHMgdGhhbiB0aGUgcHVsbCBieSByb3V0ZQoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgpDb21wYXJlIFdhaXREYXRhIChwdWxsZWQgYnkgcm91dGUpIGFuZCBvcmlnaW5hbCBkYXRhIChOZXdUcmF2VGltZSkuCmBgYHtyfQoKZGltKE5ld1RyYXZUaW1lKSAgIyAyLDgwOSw1Mjkgcm93cwpkaW0oV2FpdERhdGFfUm91dGVQdWxsKSAgIyAyLDc4MCw4NDggcm93cwpucm93KE5ld1RyYXZUaW1lKSAtIG5yb3coV2FpdERhdGFfUm91dGVQdWxsKSAgIyBpcyAyOCw2ODEgcm93cwoKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICApCiAgICkKc3RyKFdhaXREYXRhX1JvdXRlUHVsbCkKCkNvbXBhcmVfTlRUX1dEIDwtIGxlZnRfam9pbihOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChXYWl0RGF0YV9Sb3V0ZVB1bGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVHcm91cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFN0b3BJRF9DbGVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEV2ZW50X1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWluVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNYXhUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNhbXBUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5CLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX1NlYzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiUm93TnVtX09HIiA9ICJSb3dOdW1fT0ciKQogICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkKCnN0cihDb21wYXJlX05UVF9XRCkgICMgMiw4MTAsMTA5IHJvd3Mgb3ZlcmFsbCAgLS0gIDI5LDI2MSByb3dzIHdpdGggbm8gbWF0Y2gKVmlldyhoZWFkKENvbXBhcmVfTlRUX1dELCA1MDApKQpWaWV3KGZpbHRlcihDb21wYXJlX05UVF9XRCwKICAgICAgICAgICAgaXMubmEoTWluVGltZSkKICAgICAgICAgICApCiAgICApCgoKCiMgVmlldyhhbnRpX2pvaW4oU2FtcCwKIyAgICAgICAgICAgICAgICBkaXN0aW5jdChzZWxlY3QoV2FpdERhdGFfUm91dGVQdWxsLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3dOdW1fT0cKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICAgICAgIGJ5ID0gYygiUm93TnVtX09HIiA9ICJSb3dOdW1fT0ciKQojICAgICAgICAgICAgICAgKQojICAgICApCgojIFRoZSBTYW1wVGltZSBpcyBBRlRFUiB0aGUgbGFzdCBidXMgcGFzc2VkIHRoYXQgU3RvcElEX0NsZWFuCiMgVmlldyhmaWx0ZXIoU2FtcCwKIyAgICAgICAgICAgICAgIFJvdXRlID09ICJYMiIgJgojICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuID09IDEwMDM3NzQKIyAgICAgICAgICAgICAjIFJvd051bV9PRyA9IDExNDY3MjMKIyAgICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA3IDE1OjMyOjE4CiMgICAgICAgICAgICApCiMgICAgICkKCmBgYAoKCkNsZWFuIHVwIHRoZSBkYXRhIGEgYml0LgpgYGB7cn0KCnJtKEJ1c0dyb3VwcywgUm91dGVNaW5NYXgsIFNhbXAsIFRlc3RpbmcxLCBUZXN0aW5nMiwgVGVzdGluZzMsIFRlc3Rpbmc0LCBUZXN0aW5nNSwgVGVzdGluZ18zLCBUZXN0aW5nXzQsIFRlc3RpbmdfNSwgVGVzdGluZ182LCBUZXN0aW5nXzcsIFdhaXREYXRhX0RheVB1bGwsIFdhaXREYXRhX0RpZmYpCgoKc3RyKENvbXBhcmVfTlRUX1dEKQpWaWV3KGhlYWQoQ29tcGFyZV9OVFRfV0QsIDUwMCkpClZpZXcoaGVhZChtdXRhdGUoQ29tcGFyZV9OVFRfV0QsCiAgICAgICAgICAgICAgICAgV1RfTWluID0gYXMubnVtZXJpYyhXYWl0VGltZV9NaW4yKQogICAgICAgICAgICAgICAgKQogICAgICAgICApCiAgICApCgpXYWl0VGltZV9Bc051bSA8LSBDb21wYXJlX05UVF9XRCAlPiUgCiAgbXV0YXRlKFJvdXRlU3RvcF9JRCA9IGZhY3RvcihwYXN0ZShSb3V0ZSwgU3RvcElEX0NsZWFuLCBzZXAgPSAiX18iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQpXYWl0VGltZV9Bc051bSRXYWl0VGltZV9TZWMyIDwtIGFzLm51bWVyaWMoV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfU2VjMikKV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfTWluMiA8LSBhcy5udW1lcmljKFdhaXRUaW1lX0FzTnVtJFdhaXRUaW1lX01pbjIpCgpybShDb21wYXJlX05UVF9XRCkKc3RyKFdhaXRUaW1lX0FzTnVtKQoKYGBgCgoKR2VuZXJhbCBleHBsb3JhdGlvbiBvZiB3YWl0IHRpbWVzLgpgYGB7cn0KCnN1bW1hcnkoV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfTWluMikKCmBgYAoKCkdlbmVyYWwgZXhwbG9yYXRpb24gb2Ygd2FpdCB0aW1lcy4KYGBge3J9CgpXVF9RdWFudGlsZXMgPC0gYXMuZGF0YS5mcmFtZShxdWFudGlsZShXYWl0VGltZV9Bc051bSRXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCmNvbG5hbWVzKFdUX1F1YW50aWxlcykgPC0gIlZhbHVlX01pbiIKCldUX1F1YW50aWxlcyRWYWx1ZV9TZWMgPSBmb3JtYXQocm91bmQoV1RfUXVhbnRpbGVzJFZhbHVlX01pbiAqIDYwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpXVF9RdWFudGlsZXMkVmFsdWVfSHIgPSBmb3JtYXQocm91bmQoV1RfUXVhbnRpbGVzJFZhbHVlX01pbiAvIDYwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKV1RfUXVhbnRpbGVzJFZhbHVlX01pbiA9IGZvcm1hdChyb3VuZChXVF9RdWFudGlsZXMkVmFsdWVfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKV1RfUXVhbnRpbGVzJFF1YW50aWxlIDwtIHNlcSgwLCAxLCAwLjAxKQoKV1RfUXVhbnRpbGVzIDwtIHNlbGVjdChXVF9RdWFudGlsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgUXVhbnRpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgVmFsdWVfU2VjLAogICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX01pbiwKICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9IcgogICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKFdUX1F1YW50aWxlcykKVmlldyhXVF9RdWFudGlsZXMpCldUX1F1YW50aWxlcwoKClZpZXcoYXJyYW5nZShXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgIGRlc2MoV2FpdFRpbWVfTWluMikKICAgICAgICAgICAgKSAlPiUgCiAgICAgICBoZWFkKC4sIDUwMDApCiAgICApCgpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgYmV0d2VlbihXYWl0VGltZV9NaW4yLCA2MCwgMjAwKQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFdhaXRUaW1lX01pbjIpCiAgICAgICAgICAgICAgKSAKICAgICAjICU+JSAKICAgICAjICAgaGVhZCguLCA1MDAwKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIlcxMyIgJiAgIyBvbmx5IDIgYnVzIHBhc3NlcyBpbiB0aGUgZW50aXJlIGRhdGFzZXQKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMzcyOAogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTAzIDA4OjQyOjQ2CiAgICAgICAgICAgKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIlM0MSIgJiAgIyBvbmx5IDQgYnVzIHBhc3NlcyBpbiB0aGUgZW50aXJlIGRhdGFzZXQKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMTA5NQogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA1IDE1OjQxOjQ3CiAgICAgICAgICAgKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIkQ4IiAmICAjIHJvdXRlIGhhcyBWRVJZIGxpbWl0ZWQgc2VydmljZSBhZnRlciBtaWRuaWdodAogICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiA9PSAxMDAxNjY5CiAgICAgICAgICAgICMgRXZlbnRfVGltZSA9IDIwMTYtMTAtMDYgMjA6MzE6MTYKICAgICAgICAgICApCiAgICApCgpgYGAKCgpMb29rcyBsaWtlIHRoZXJlIG1pZ2h0IGJlIGFuIGlzc3VlIGluIHdhaXQgdGltZXMgd2hlbiB2ZXJ5IGZldyBSb3V0ZS1TdG9wIGNvbWJpbmF0aW9ucyBhcmUgaW5jbHVkZWQgaW4gdGhlIGRhdGFzZXQuICBMZXQncyBleHBsb3JlIHRoZXNlLgpgYGB7cn0KClJvdXRlU3RvcF9DbnRzIDwtIGdyb3VwX2J5KFdhaXRUaW1lX0FzTnVtLAogICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZVN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUm91dGVTdG9wX0NudE51bSA9IG4oKSwKICAgICAgICAgICAgUm91dGVTdG9wX0NudFBjdCA9IFJvdXRlU3RvcF9DbnROdW0gLyBucm93KFdhaXRUaW1lX0FzTnVtKQogICAgICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGVTdG9wX0NudE51bSkKClZpZXcoUm91dGVTdG9wX0NudHMpCgoKUm91dGVTdG9wX0NudE9mQ250IDwtIGdyb3VwX2J5KFJvdXRlU3RvcF9DbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVTdG9wX0NudE51bQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUm91dGVTdG9wQ250X0NudE51bSA9IG4oKSwKICAgICAgICAgICAgUm91dGVTdG9wQ250X0NudFBjdCA9IFJvdXRlU3RvcENudF9DbnROdW0gLyBucm93KFJvdXRlU3RvcF9DbnRzKQogICAgICAgICAgICkgJT4lIAogIG11dGF0ZShSb3V0ZVN0b3BDbnRfQ250UGN0X0N1bVN1bSA9IGN1bXN1bShSb3V0ZVN0b3BDbnRfQ250UGN0KSwKICAgICAgICAgeCA9IDEgLSBSb3V0ZVN0b3BDbnRfQ250UGN0X0N1bVN1bQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGVTdG9wX0NudE51bSkKICAKIFZpZXcoUm91dGVTdG9wX0NudE9mQ250KQogUm91dGVTdG9wX0NudE9mQ250CgpgYGAKCgpIaXN0b2dyYW0gb2YgdGhlIGNvdW50cyBvZiBSb3V0ZS1TdG9wSUQgY29tYmluYXRpb25zLgpgYGB7cn0KClJvdXRlU3RvcF9DbnRzX0JhciA8LSBnZ3Bsb3QoUm91dGVTdG9wX0NudE9mQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gUm91dGVTdG9wX0NudE51bSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFJvdXRlU3RvcENudF9DbnROdW0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICAjIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fY29sKGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNTAwKQogICAgICAgICAgICAgICAgICAjIHlsaW0gPSBjKDAsIDAuMDIpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gUm91dGVzIFBhc3NpbmcgYSBTcGVjaWZpYyBTdG9wIiwKICAgICAgIHggPSAiT2NjdXJyZW5jZXMgb2YgUm91dGUtU3RvcElEIENvbWJpYW50aW9ucyIsCiAgICAgICB5ID0gIkNvdW50cyIKICAgICAgKQoKUm91dGVTdG9wX0NudHNfQmFyCgpgYGAKCgpDcmVhdGUgYSBuZXcgZGF0YXNldCBsaW1pdGluZyBleHRyZW1lbHkgc21hbGwgY291bnRzIG9mIFJvdXRlLVN0b3BJRCBjb21iaW5hdGlvbnMuCmBgYHtyfQoKV2FpdFRpbWVfUnRlQ250cyA8LSBsZWZ0X2pvaW4oV2FpdFRpbWVfQXNOdW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvdXRlU3RvcF9DbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlJvdXRlU3RvcF9JRCIgPSAiUm91dGVTdG9wX0lEIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoLVJvdXRlU3RvcF9DbnRQY3QpCgpkaW0oV2FpdFRpbWVfQXNOdW0pCmRpbShXYWl0VGltZV9SdGVDbnRzKQoKcm0oV2FpdFRpbWVfQXNOdW0pCnN0cihXYWl0VGltZV9SdGVDbnRzKQoKCiMgVG90YWwgcm93cwpucm93KFdhaXRUaW1lX1J0ZUNudHMpCgojIFJvd3Mgb2YgcmFyZSBSb3V0ZVN0b3BzCm5yb3coZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgIFJvdXRlU3RvcF9DbnROdW0gPD0gNjAKICAgICAgICAgICApCiAgICApIC8gbnJvdyhXYWl0VGltZV9SdGVDbnRzKQoKIyBSb3dzIG9mIGV4dHJlbWVseSBsb25nIHdhaXQgdGltZXMKbnJvdyhmaWx0ZXIoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgV2FpdFRpbWVfTWluMiA+IDE4MAogICAgICAgICAgICkKICAgICkgLyBucm93KFdhaXRUaW1lX1J0ZUNudHMpCgoKc2VsZWN0KFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICBXYWl0VGltZV9NaW4yCiAgICAgICkgJT4lIAogIHN1bW1hcnkoKQoKZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICBSb3V0ZVN0b3BfQ250TnVtID4gNjAgICMgMTIgcGFzc2VzIHBlciBkYXkgaW4gYSA1LWRheSBkYXRhc2V0CiAgICAgICkgJT4lIAogIHNlbGVjdChXYWl0VGltZV9NaW4yKSAlPiUgCiAgc3VtbWFyeSgpCgpmaWx0ZXIoV2FpdFRpbWVfUnRlQ250cywKICAgICAgIFdhaXRUaW1lX01pbjIgPCAxODAgICMgcHJvYmFibHkgbWVhbnMgdGhhdCBzb21ldGhpbmcgd2VudCB3cm9uZwogICAgICApICU+JSAKICBzZWxlY3QoV2FpdFRpbWVfTWluMikgJT4lIAogIHN1bW1hcnkoKQoKYGBgCgoKQ29tcGFyZSBxdWFudGlsZXMgaW4gdGhlIGxpbWl0ZWQgZGF0YXNldHMuCmBgYHtyfQoKYSA8LSBhcy5kYXRhLmZyYW1lKHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICApCgpiIDwtIGFzLmRhdGEuZnJhbWUoZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVTdG9wX0NudE51bSA+IDYwCiAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFdhaXRUaW1lX01pbjIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUocHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgKQoKYyA8LSBhcy5kYXRhLmZyYW1lKGZpbHRlcihXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIgPCAxODAKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICAgICAgICBzZWxlY3QoV2FpdFRpbWVfTWluMikgJT4lIAogICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICApCgpXVF9GaWx0ZXJfUXVhbnRpbGVzIDwtIGJpbmRfY29scyhhLCBiLCBjKSAlPiUgCiAgbXV0YXRlKFF1YW50aWxlID0gc2VxKDAsIDEsIDAuMDEpCiAgICAgICAgKQoKY29sbmFtZXMoV1RfRmlsdGVyX1F1YW50aWxlcykgPC0gYygiQWxsIiwgIlJ0ZVN0cEFidjYwIiwgIldUQmx3MTgwIiwgIlF1YW50aWxlIikKcm0oYSwgYiwgYykKVmlldyhXVF9GaWx0ZXJfUXVhbnRpbGVzKQpXVF9GaWx0ZXJfUXVhbnRpbGVzCgpgYGAKCgpIaXN0b2dyYW0gb2YgYWxsIHdhaXQgdGltZXMuCmBgYHtyfQoKV2FpdFRpbWVfQWxsQnVzX0hpc3REZW4gPC0gZ2dwbG90KGZpbHRlcihzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFdhaXRUaW1lX01pbjIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAuLmRlbnNpdHkuLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fbGluZShzdGF0ID0gImRlbnNpdHkiLCBjb2xvdXIgPSAicmVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMzAwLCAzMCkKICAgICAgICAgICAgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMzAwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMC4wMzUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gV2FpdCBUaW1lIiwKICAgICAgIHggPSAiV2FpdCBUaW1lIChtaW4pIiwKICAgICAgIHkgPSAiRGVuc2l0eSIKICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX0hpc3REZW4KCmBgYAoKCkJveCBwbG90cyBmb3IgV2FpdFRpbWUgKGFsbCBidXNzZXMsIGJ5IFppcCBDb2RlKS4KYGBge3J9CgojIENvdW50X1ZhbHVlcyBpcyBuZWVkZWQgdG8gZGlzcGxheSB0aGUgbWVkaWFucyBvbiB0aGUgYm94IHBsb3RzCkJ1c1JvdXRlIDwtIHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFJvdXRlID09ICJYMiIpCgpDb3VudFZhbHVlc19BbGxCdXNfWmlwIDwtIGRkcGx5KEJ1c1JvdXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4oU3RvcF9aaXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9Db3VudHMgPSBtZWRpYW4oV2FpdFRpbWVfTWluMiwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX1ppcF9Cb3ggPC0gZ2dwbG90KEJ1c1JvdXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmFjdG9yKFN0b3BfWmlwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFLCBuYS5ybSA9IFRSVUUpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50VmFsdWVzX0FsbEJ1c19aaXAsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgYXQgYSBHaXZlbiBTdG9wIChmb3IgdGhlIFgyKSIsCiAgICAgICB4ID0gIlppcCBDb2RlIG9mIERlc3RpbmF0aW9uIiwKICAgICAgIHkgPSAiV2FpdGluZyBUaW1lIChtaW4pIgogICAgICApCgpXYWl0VGltZV9BbGxCdXNfWmlwX0JveAoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBWaW9saW4gcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IFppcCBDb2RlKS4KYGBge3J9CgpXYWl0VGltZV9BbGxCdXNfWmlwX1Zpb2xpbiA8LSBnZ3Bsb3QoQnVzUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKFN0b3BfWmlwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoU3RvcF9aaXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoMC4yNSwgMC41LCAwLjc1KSwKICAgICAgICAgICAgICB0cmltID0gVFJVRSwKICAgICAgICAgICAgICBzY2FsZSA9ICJjb3VudCIsCiAgICAgICAgICAgICAgbmEucm0gPSBUUlVFLAogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gTkEsCiAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBUUlVFCiAgICAgICAgICAgICApICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50VmFsdWVzX0FsbEJ1c19aaXAsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMy41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCA0NSkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AgKGZvciB0aGUgWDIpIiwKICAgICAgIHggPSAiWmlwIENvZGUgb2YgRGVzdGluYXRpb24iLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdF96CgpgYGAKCgpCb3ggcGxvdHMgZm9yIFdhaXRUaW1lIChaaXAgQ29kZSwgYnkgSG91ckdyb3VwWmlwKS4KYGBge3J9CgojIENvdW50X1ZhbHVlcyBpcyBuZWVkZWQgdG8gZGlzcGxheSB0aGUgbWVkaWFucyBvbiB0aGUgYm94IHBsb3RzClppcCA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgIFN0b3BfWmlwLAogICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFN0b3BfWmlwID09IDIwMDAyKQoKQ291bnRWYWx1ZXNfQWxsQnVzX0hHIDwtIGRkcGx5KFppcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4oRXZlbnRfVGltZV9Ickdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX0NvdW50cyA9IG1lZGlhbihXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX0hHX0JveCA8LSBnZ3Bsb3QoWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFLCBuYS5ybSA9IFRSVUUpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50VmFsdWVzX0FsbEJ1c19IRywKICAgICAgICAgICAgYWVzKHkgPSBWYWx1ZV9Db3VudHMsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdChyb3VuZChWYWx1ZV9Db3VudHMsIGRpZ2l0cyA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIHNpemUgPSAyLjUsCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNQogICAgICAgICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGNvb3JkX2NhcnRlc2lhbigjIHhsaW0gPSBjKDAsIDE4MCksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDQ1KQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiV2FpdGluZyBUaW1lIGF0IGEgR2l2ZW4gU3RvcCAoZm9yIFppcCAyMDAwMikiLAogICAgICAgeCA9ICJIb3VyIEdyb3VwIiwKICAgICAgIHkgPSAiV2FpdGluZyBUaW1lIChtaW4pIgogICAgICApCiAgIyBmYWNldF93cmFwKH5TdG9wX1ppcAogICMgICAgICAgICAgICAjIG5yb3cgPSA1CiAgIyAgICAgICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX0hHX0JveAoKYGBgCgoKVmlvbGluIHBsb3RzIGZvciBXYWl0VGltZSAoWmlwIENvZGUsIGJ5IEhvdXJHcm91cFppcCkuCmBgYHtyfQoKV2FpdFRpbWVfQWxsQnVzX0hHX1ZsbiA8LSBnZ3Bsb3QoWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV92aW9saW4oZHJhd19xdWFudGlsZXMgPSBjKDAuMjUsIDAuNSwgMC43NSksCiAgICAgICAgICAgICAgdHJpbSA9IFRSVUUsCiAgICAgICAgICAgICAgc2NhbGUgPSAiY291bnQiLAogICAgICAgICAgICAgIG5hLnJtID0gVFJVRSwKICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IE5BLAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gVFJVRQogICAgICAgICAgICAgKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfSEcsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCA5MCkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AgKGZvciBaaXAgMjAwMDIpIiwKICAgICAgIHggPSAiSG91ciBHcm91cCIsCiAgICAgICB5ID0gIldhaXRpbmcgVGltZSAobWluKSIKICAgICAgKQogICMgZmFjZXRfd3JhcCh+U3RvcF9aaXAKICAjICAgICAgICAgICAgIyBucm93ID0gNQogICMgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19IR19WbG4KCmBgYAoKCkJveCBwbG90cyBmb3IgV2FpdFRpbWUgKFJvdXRlLCBieSBIb3VyR3JvdXBaaXApLgpgYGB7cn0KCiMgQ291bnRfVmFsdWVzIGlzIG5lZWRlZCB0byBkaXNwbGF5IHRoZSBtZWRpYW5zIG9uIHRoZSBib3ggcGxvdHMKUnRlIDwtIHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgIFJvdXRlLAogICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIsCiAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGUgPT0gIlgyIikKCkNvdW50VmFsdWVzX0FsbEJ1c19SdGVIRyA8LSBncm91cF9ieShSdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKFdhaXRUaW1lX01pbjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgVkMgPSBxdWFudGlsZShXYWl0VGltZV9NaW4yLCBwcm9icyA9IDAuOSwgbmEucm0gPSBUUlVFKQogICAgKQoKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19Cb3ggPC0gZ2dwbG90KFJ0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91cj0icmVkIiwgbm90Y2g9VFJVRSwgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfUnRlSEcsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCBtYXgoQ291bnRWYWx1ZXNfQWxsQnVzX1J0ZUhHJFZDKSkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgc3VidGl0bGUgPSAoIlJvdXRlIFgyIiksCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkgCiMgKwojICAgZmFjZXRfd3JhcCh+U3RvcF9aaXAKIyAgICAgICAgICAgICAgIyBucm93ID0gNQojICAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19Cb3gKCmBgYAoKClZpb2xpbiBwbG90cyBmb3IgV2FpdFRpbWUgKFppcCBDb2RlLCBieSBIb3VyR3JvdXBaaXApLgpgYGB7cn0KCldhaXRUaW1lX0FsbEJ1c19SdGVIR19WbG4gPC0gZ2dwbG90KFJ0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYygwLjI1LCAwLjUsIDAuNzUpLAogICAgICAgICAgICAgIHRyaW0gPSBUUlVFLAogICAgICAgICAgICAgIHNjYWxlID0gImNvdW50IiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsCiAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBOQSwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IFRSVUUKICAgICAgICAgICAgICkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRWYWx1ZXNfQWxsQnVzX1J0ZUhHLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHN1YnRpdGxlID0gKCIoUm91dGUgWDIpIiksCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkgKwogIGZhY2V0X3dyYXAoflN0b3BfWmlwCiAgICAgICAgICAgICAjIG5yb3cgPSA1CiAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19WbG4KCmBgYAoKClgyIFBlcmNlbnRpbGVzIExpbmUgR3JhcGggVGVzdC4KYGBge3J9CgpYMl9QY3QgPC0gc2VsZWN0KFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5LAogICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cCwKICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyLAogICAgICAgICAgICAgICAgIExhdGl0dWRlLAogICAgICAgICAgICAgICAgIExvbmdpdHVkZSwKICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yCiAgICAgICAgICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGUgPT0gIlgyIikgJT4lIAogIGdyb3VwX2J5KEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShQY3Q1MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC41LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q2MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC42LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q3MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC43LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q4MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC44LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q5MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC45LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKQoKc3RyKFgyX1BjdCkKVmlldyhYMl9QY3QpCgoKWDJfTG9uZyA8LSBnYXRoZXIoWDJfUGN0LAogICAgICAgICAgICAgICAgICBrZXkgPSBQZXJjZW50aWxlLAogICAgICAgICAgICAgICAgICB2YWx1ZSA9IFBjdGlsZSwKICAgICAgICAgICAgICAgICAgUGN0NTAsCiAgICAgICAgICAgICAgICAgIFBjdDYwLAogICAgICAgICAgICAgICAgICBQY3Q3MCwKICAgICAgICAgICAgICAgICAgUGN0ODAsCiAgICAgICAgICAgICAgICAgIFBjdDkwCiAgICAgICAgICAgICAgICApCgpzdHIoWDJfTG9uZykKVmlldyhYMl9Mb25nKQoKClgyX1dhaXRCeUhyX0xpbmUgPC0gZ2dwbG90KFgyX0xvbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gRXZlbnRfVGltZV9IciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBQY3RpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWN0b3IoUGVyY2VudGlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fbGluZSgpICsKICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iCiAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMjMpCiAgICAgICAgICAgICAgICAgICMgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMjMsIDIpCiAgICAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgVGhyb3VnaG91dCB0aGUgRGF5IiwKICAgICAgIHN1YnRpdGxlID0gKCIoUm91dGUgWDIpIiksCiAgICAgICB4ID0gIkhvdXIgb2YgdGhlIERheSIsCiAgICAgICB5ID0gIldhaXRpbmcgVGltZSAobWluKSIKICAgICAgKSArCiAgZmFjZXRfd3JhcCh+U3RvcF9aaXApCgpYMl9XYWl0QnlIcl9MaW5lCgpgYGAKCgoKR0VUIERBVEEgUkVBRFkgRk9SIFNISU5ZICAtLSAgR0VUIERBVEEgUkVBRFkgRk9SIFNISU5ZICAtLSAgR0VUIERBVEEgUkVBRFkgRk9SIFNISU5ZCkdFVCBEQVRBIFJFQURZIEZPUiBTSElOWSAgLS0gIEdFVCBEQVRBIFJFQURZIEZPUiBTSElOWSAgLS0gIEdFVCBEQVRBIFJFQURZIEZPUiBTSElOWQpHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkgIC0tICBHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkgIC0tICBHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkKCkJhc2VEYXRhOiBVc2VkIGluIHBsb3RzIGJ5IGhvdXIgYW5kIHppcGNvZGUgKGZpcnN0IHR3byBTaGlueSB0YWJzKS4KYGBge3J9CgojIHN0cihXYWl0VGltZV9SdGVDbnRzKQoKU2hpbnlfV2FpdERhdGFfQmFzZSA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExhdGl0dWRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMb25naXR1ZGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoRXZlbnRfVGltZV9Zck10aERheUhyID0gZmxvb3JfZGF0ZShFdmVudF9UaW1lLCAiaG91ciIpCiAgICAgICAgKSAlPiUgCiAgcmVuYW1lKFppcENvZGUgPSBTdG9wX1ppcCwKICAgICAgICAgSG91ckdyb3VwID0gRXZlbnRfVGltZV9Ickdyb3VwLAogICAgICAgICBEYXRlID0gRXZlbnRfVGltZV9EYXRlLAogICAgICAgICBEYXkgPSBFdmVudF9UaW1lX0RheSwKICAgICAgICAgSG91ciA9IEV2ZW50X1RpbWVfSHIsCiAgICAgICAgIFdhaXRUaW1lX01pbiA9IFdhaXRUaW1lX01pbjIKICAgICAgICApICU+JSAKICBmaWx0ZXIoV2FpdFRpbWVfTWluIDw9IDE4MCkKClNoaW55X1dhaXREYXRhX0Jhc2UkUm91dGUgPC0gZmFjdG9yKFNoaW55X1dhaXREYXRhX0Jhc2UkUm91dGUpCgpzdHIoU2hpbnlfV2FpdERhdGFfQmFzZSkKVmlldyh0YWlsKFNoaW55X1dhaXREYXRhX0Jhc2UsIDUwMCkpCgpzYXZlUkRTKFNoaW55X1dhaXREYXRhX0Jhc2UsCiAgICAgICAgIlNoaW55X1dhaXREYXRhX0Jhc2UucmRzIgogICAgICAgKQoKYGBgCgoKUHJlcCBkYXRhIGZvciBtYXBwaW5nLgpgYGB7cn0KCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJka2FobGUvZ2dtYXAiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiaGFkbGV5L2dncGxvdDIiKQojIGluc3RhbGwucGFja2FnZXMoImdnbWFwIiwgdHlwZSA9ICJzb3VyY2UiKQoKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ2hhZGxleS9nZ3Bsb3QyJykKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJoYWRsZXkvZ2dwbG90MkB2Mi4yLjAiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigndGhvbWFzcDg1L2dnZm9yY2UnKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigndGhvbWFzcDg1L2dncmFwaCcpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdzbG93a293L2dncmVwZWwnKQoKCnRyYWN0IDwtIAogIHJlYWRPR1IoZHNuID0gIi9Vc2Vycy9tZHR1cnNlL0Rlc2t0b3AvQW5hbHl0aWNzL0RDTWV0cm9CdXMvdGxfMjAxNl91c196Y3RhNTEwIiwKICAgICAgICAgIGxheWVyID0gInRsXzIwMTZfdXNfemN0YTUxMCIKICAgICAgICAgKQogIApjbGFzcyh0cmFjdCkKCiMgY29udmVydCB0aGUgR0VPSUQgdG8gYSBjaGFyYWN0ZXIKdHJhY3RAZGF0YSRHRU9JRCA8LSBhcy5jaGFyYWN0ZXIodHJhY3RAZGF0YSRHRU9JRCkKc3RyKHRyYWN0QGRhdGEpCgoKZ2d0cmFjdCA8LSB0aWR5KHRyYWN0LCByZWdpb24gPSAiR0VPSUQiKQoKIyBzdHIoZ2d0cmFjdCkKIyBzdW1tYXJ5KGdndHJhY3QpCiMgVmlldyhoZWFkKGdndHJhY3QsIDUwKSkKCgoKIyBzdHIoU2hpbnlfV2FpdERhdGFfQmFzZSkKClppcFdhaXRUZXN0IDwtIGZpbHRlcihTaGlueV9XYWl0RGF0YV9CYXNlLAogICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluIDw9IDE4MCAmCiAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShaaXBDb2RlKQogICAgICAgICAgICAgICAgICAgICApICU+JSAKICBncm91cF9ieShaaXBDb2RlLAogICAgICAgICAgIEV2ZW50X1RpbWVfWXJNdGhEYXlIcgogICAgICAgICAgICMgRXZlbnRfVGltZV9EYXksCiAgICAgICAgICAgIyBFdmVudF9UaW1lX0hyCiAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUGN0ODAgPSBxdWFudGlsZShXYWl0VGltZV9NaW4sIHByb2JzID0gMC44LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZSgjIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICBaaXBDb2RlLAogICAgICAgICAgRXZlbnRfVGltZV9Zck10aERheUhyCiAgICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgbXV0YXRlKEV2ZW50X1RpbWVfRGF0ZU5ldyA9IGZsb29yX2RhdGUoRXZlbnRfVGltZV9Zck10aERheUhyLCAiZGF5IiksCiAgICAgICAgIEV2ZW50X1RpbWVfSHJOZXcgPSBob3VyKEV2ZW50X1RpbWVfWXJNdGhEYXlIciksCiAgICAgICAgIFBjdDgwX0xldmVsID0gZmFjdG9yKGlmZWxzZShQY3Q4MCA8IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJlbG93IDEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBjdDgwIDwgMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmVsb3cgMjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUGN0ODAgPCAzMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZWxvdyAzMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShQY3Q4MCA8IDQwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJlbG93IDQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBjdDgwIDwgNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmVsb3cgNTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUGN0ODAgPCA2MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZWxvdyA2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNjAgUGx1cyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJCZWxvdyAxMCIsICJCZWxvdyAyMCIsICJCZWxvdyAzMCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZWxvdyA0MCIsICJCZWxvdyA1MCIsICJCZWxvdyA2MCIsICI2MCBQbHVzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKc3RyKFppcFdhaXRUZXN0KQpaaXBXYWl0VGVzdCRaaXBDb2RlIDwtIGFzLmNoYXJhY3RlcihaaXBXYWl0VGVzdCRaaXBDb2RlKQpzdHIoWmlwV2FpdFRlc3QpCnN1bW1hcnkoWmlwV2FpdFRlc3QpCgpWaWV3KGhlYWQoWmlwV2FpdFRlc3QsIDUwMCkpCgoKU3RvcFppcF9MZWZ0IDwtIGxlZnRfam9pbihaaXBXYWl0VGVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3RyYWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiWmlwQ29kZSIgPSAiaWQiKQogICAgICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKFN0b3BaaXBfTGVmdCkKc3VtbWFyeShTdG9wWmlwX0xlZnQpCgpgYGAKCgpUZXN0IG1hcHBpbmcgZnVuY3Rpb25hbHRpeS4KYGBge3J9CgptYXAgPC0gZ2V0X21hcChsb2NhdGlvbiA9IGMobG9uID0gLTc3LjAzNjc2LCBsYXQgPSAzOC44OTc4NCksCiAgICAgICAgICAgICAgIHNvdXJjZSA9ICJnb29nbGUiLAogICAgICAgICAgICAgICAjIG1hcHR5cGUgPSAicm9hZG1hcCIKICAgICAgICAgICAgICAgem9vbSA9IDEyCiAgICAgICAgICAgICAgKQoKZ2dtYXAobWFwKSArCiAgZ2VvbV9wb2x5Z29uKGFlcyh4ID0gbG9uZywgCiAgICAgICAgICAgICAgICAgICB5ID0gbGF0LCAKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXAsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gUGN0ODBfTGV2ZWwKICAgICAgICAgICAgICAgICAgKSwgCiAgICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXIoU3RvcFppcF9MZWZ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfWXJNdGhEYXlIciA9PSBhcy5QT1NJWGN0KCIyMDE2LTEwLTA3IDIwOjAwOjAwIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgU3RvcF9aaXAgPT0gIjIwMDAzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgY29sb3VyID0gImdyYXkxIiwgCiAgICAgICAgICAgICAgICMgZmlsbCA9ICdibGFjaycsIAogICAgICAgICAgICAgICBhbHBoYSA9IC40LCAKICAgICAgICAgICAgICAgc2l6ZSA9IC4zCiAgICAgICAgICAgICAgKSArCiMgKwogICMgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGMoIndoaXRlIiwgInJveWFsYmx1ZTQiLCAicmVkIiksCiAgIyAgICAgICAgICAgICAgICAgICAgICAjICAibGlnaHRzdGVlbGJsdWU0IiwKICAjICAgICAgICAgICAgICAgICAgICAgICMgImxpZ2h0cGluazEiLAogICMgICAgICAgICAgICAgICAgICAgICAgIyB2YWx1ZXM9Y2JQYWxldHRlLAogICMgICAgICAgICAgICAgICAgICAgICAgIyB2YWx1ZXMgPSBjKDEsMC41LCAuMywgLjIsIC4xLCAwKQogICMgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSAiYmxhY2siLAogICMgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhzZXEoMCwgMTgwLCAzMCkpCiAgIyAgICAgICAgICAgICAgICAgICAgICAjIHZhbHVlcyA9IHJlc2NhbGUoKQogICMgICAgICAgICAgICAgICAgICAgICApIAojICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNwZWN0cmFsIiwgIyAiWWxPclJkIiAjICJTZXQxIiwKICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAtMSwKICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBsZXZlbHMoU3RvcFppcF9MZWZ0JFBjdDgwX0xldmVsKQogICAgICAgICAgICAgICAgICAgKQoKYGBgCgoKU2hpbnkgZGF0YSBmb3IgbWFwcGluZyAodXNlZCBpbiAzcmQgdGFiKS4KYGBge3J9CgpWaWV3KGhlYWQoZmlsdGVyKFN0b3BaaXBfTGVmdCwKICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyTmV3ID09IDE1CiAgICAgICAgICAgICAgICApLAogICAgICAgICAgNTAwCiAgICAgICAgICkKICAgICkKClNoaW55X1dhaXREYXRhX01hcCA8LSBTdG9wWmlwX0xlZnQgJT4lIAogIHJlbmFtZShZck10aERheUhyID0gRXZlbnRfVGltZV9Zck10aERheUhyLAogICAgICAgICBZck10aERheSA9IEV2ZW50X1RpbWVfRGF0ZU5ldywKICAgICAgICAgSG91ciA9IEV2ZW50X1RpbWVfSHJOZXcKICAgICAgICApCgpzdHIoU2hpbnlfV2FpdERhdGFfTWFwKQoKClNoaW55X1dhaXREYXRhX01hcF9XZWQgPC0gZmlsdGVyKFNoaW55X1dhaXREYXRhX01hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWXJNdGhEYXkgPT0gYXMuUE9TSVhjdCgiMjAxNi0xMC0wNSIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKFNoaW55X1dhaXREYXRhX01hcF9XZWQpCnN1bW1hcnkoU2hpbnlfV2FpdERhdGFfTWFwX1dlZCkKCgpzYXZlUkRTKFNoaW55X1dhaXREYXRhX01hcCwKICAgICAgICAiU2hpbnlfV2FpdERhdGFfTWFwLnJkcyIKICAgICAgICkKCnNhdmVSRFMoU2hpbnlfV2FpdERhdGFfTWFwX1dlZCwKICAgICAgICAiU2hpbnlfV2FpdERhdGFfTWFwX1dlZC5yZHMiCiAgICAgICApCgpgYGAKCgoKCkNsdXN0ZXJpbmcKCkRhdGEgcHJlcC4KYGBge3J9CgpybSh0cmFjdCwgZ2d0cmFjdCwgU3RvcFppcF9MZWZ0LCBaaXBXYWl0VGVzdCwgU2hpbnlfV2FpdERhdGFfQmFzZSwgU2hpbnlfV2FpdERhdGFfTWFwLCBTaGlueV9XYWl0RGF0YV9NYXBfV2VkKQoKCmRpbShOZXdUcmF2VGltZSkKZGltKFdhaXRUaW1lX1J0ZUNudHMpCgoKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICApCiAgICkKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICBtYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICkKICAgKQpzdHIoV2FpdFRpbWVfUnRlQ250cykKCiAKIAojIENsdXN0RGF0YSA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKIyAgICAgICAgICAgICAgICAgICAgIGdyb3VwLAojICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtLAojICAgICAgICAgICAgICAgICAgICAgUm91dGUsCiMgICAgICAgICAgICAgICAgICAgICBSdGVDaGFuZ2UyLAojICAgICAgICAgICAgICAgICAgICAgUm91dGVBbHQsCiMgICAgICAgICAgICAgICAgICAgICBEaXJDaGFuZ2UyLAojICAgICAgICAgICAgICAgICAgICAgUm91dGVfRGlyZWN0aW9uLAojICAgICAgICAgICAgICAgICAgICAgU3RvcF9TZXF1ZW5jZSwKIyAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9JbmRpY2F0b3IsCiMgICAgICAgICAgICAgICAgICAgICBTdG9wX0NvdW50eSwKIyAgICAgICAgICAgICAgICAgICAgIFN0b3BfQ2l0eSwKIyAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwLAojICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9IciwKIyAgICAgICAgICAgICAgICAgICAgIER3ZWxsX1RpbWUyLAojICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywKIyAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnNfTGFiZWwsCiMgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfTmV3LAojICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCwKIyAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKIyAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiMgICBmaWx0ZXIoV2FpdFRpbWVfTWluMiA8PSAxODApICU+JSAKIyAgIG11dGF0ZShTcGVlZEF2Z19NcGhfVERNTkhfVFRTTiA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMgLyAKIyAgICAgICAgICAgIChUVF9TZWNfTmV3IC8gNjAgLyA2MCkKIyAgICAgICAgICkKIyAlPiUgCiMgICBzZWxlY3RfaWYoQ2x1c3REYXRhLAojICAgICAgICAgICAgIGZ1bmN0aW9uKGNvbCkgaXMubnVtZXJpYyhjb2wpIHwKIyAgICAgICAgICAgICAgIGlzLmludGVnZXIoY29sKQojICAgICAgICAgICAgKSAlPiUgCiMgICAgc2NhbGUoKQoKIyBzdHIoQ2x1c3REYXRhKQojIFZpZXcodGFpbChDbHVzdERhdGEsIDUwMCkpCiMgcm93bmFtZXMoQ2x1c3REYXRhKSA8LSBDbHVzdERhdGEkUm91dGUKIyBDbHVzdERhdGEkUm91dGUgPC0gYXMuZmFjdG9yKENsdXN0RGF0YSRSb3V0ZSkKIyBzdHIoQ2x1c3REYXRhKQojIGhlYWQoQ2x1c3REYXRhKQoKClJvdXRlU3RhdHMgPC0gZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIgPD0gMTgwCiAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFNwZWVkQXZnX01waF9URE1OSF9UVFNOID0gVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyAvIChUVF9TZWNfTmV3IC8gNjAgLyA2MCkKICAgICAgICApICU+JSAKICBncm91cF9ieShSb3V0ZSkgJT4lIAogIHN1bW1hcmlzZShCdXNEYXlFdmVudE51bV9NZWFuID0gbWVhbihCdXNEYXlfRXZlbnROdW0sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDEwID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDI1ID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDUwID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuNTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDc1ID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDkwID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuOTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFN0b3BTZXF1ZW5jZV9NZWFuID0gbWVhbihTdG9wX1NlcXVlbmNlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBTdG9wU2VxdWVuY2VfUGN0MTAgPSBxdWFudGlsZShTdG9wX1NlcXVlbmNlLCBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFN0b3BTZXF1ZW5jZV9QY3QyNSA9IHF1YW50aWxlKFN0b3BfU2VxdWVuY2UsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgU3RvcFNlcXVlbmNlX1BjdDUwID0gcXVhbnRpbGUoU3RvcF9TZXF1ZW5jZSwgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBTdG9wU2VxdWVuY2VfUGN0NzUgPSBxdWFudGlsZShTdG9wX1NlcXVlbmNlLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFN0b3BTZXF1ZW5jZV9QY3Q5MCA9IHF1YW50aWxlKFN0b3BfU2VxdWVuY2UsIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRXZlbnRUaW1lSHJfTWVhbiA9IG1lYW4oRXZlbnRfVGltZV9IciwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRXZlbnRUaW1lSHJfUGN0MTAgPSBxdWFudGlsZShFdmVudF9UaW1lX0hyLCBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEV2ZW50VGltZUhyX1BjdDI1ID0gcXVhbnRpbGUoRXZlbnRfVGltZV9IciwgcHJvYnMgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBFdmVudFRpbWVIcl9QY3Q1MCA9IHF1YW50aWxlKEV2ZW50X1RpbWVfSHIsIHByb2JzID0gMC41MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRXZlbnRUaW1lSHJfUGN0NzUgPSBxdWFudGlsZShFdmVudF9UaW1lX0hyLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEV2ZW50VGltZUhyX1BjdDkwID0gcXVhbnRpbGUoRXZlbnRfVGltZV9IciwgcHJvYnMgPSAwLjkwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBEd2VsbFRpbWUyX01lYW4gPSBtZWFuKER3ZWxsX1RpbWUyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBEd2VsbFRpbWUyX1BjdDEwID0gcXVhbnRpbGUoRHdlbGxfVGltZTIsIHByb2JzID0gMC4xMCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRHdlbGxUaW1lMl9QY3QyNSA9IHF1YW50aWxlKER3ZWxsX1RpbWUyLCBwcm9icyA9IDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIER3ZWxsVGltZTJfUGN0NTAgPSBxdWFudGlsZShEd2VsbF9UaW1lMiwgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBEd2VsbFRpbWUyX1BjdDc1ID0gcXVhbnRpbGUoRHdlbGxfVGltZTIsIHByb2JzID0gMC43NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRHdlbGxUaW1lMl9QY3Q5MCA9IHF1YW50aWxlKER3ZWxsX1RpbWUyLCBwcm9icyA9IDAuOTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFRyYXZEaXN0TWlfTWVhbiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdkRpc3RNaV9QY3QxMCA9IHF1YW50aWxlKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICBUcmF2RGlzdE1pX1BjdDI1ID0gcXVhbnRpbGUoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIFRyYXZEaXN0TWlfUGN0NTAgPSBxdWFudGlsZShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgVHJhdkRpc3RNaV9QY3Q3NSA9IHF1YW50aWxlKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICBUcmF2RGlzdE1pX1BjdDkwID0gcXVhbnRpbGUoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIFRyYXZUaW1TZWNfTWVhbiA9IG1lYW4oVFRfU2VjX05ldywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3QxMCA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC4xMCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3QyNSA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3Q1MCA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC41MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3Q3NSA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC43NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3Q5MCA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgV2FpdFRpbU1pbl9NZWFuID0gbWVhbihXYWl0VGltZV9NaW4yLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDEwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjEwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDI1ID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDUwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDc1ID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjc1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDkwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjkwLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgpzdHIoUm91dGVTdGF0cykKCnJvd25hbWVzKFJvdXRlU3RhdHMpIDwtIFJvdXRlU3RhdHMkUm91dGUKc3RyKFJvdXRlU3RhdHMpClZpZXcoUm91dGVTdGF0cykKCgpSb3V0ZVN0YXRzX1NjYWxlZCA8LSBzZWxlY3QoUm91dGVTdGF0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzY2FsZSgpCgpzdHIoUm91dGVTdGF0c19TY2FsZWQpCmNsYXNzKFJvdXRlU3RhdHNfU2NhbGVkKQpWaWV3KFJvdXRlU3RhdHNfU2NhbGVkKQoKc3VtbWFyeShSb3V0ZVN0YXRzKQpzdW1tYXJ5KFJvdXRlU3RhdHNfU2NhbGVkKQoKIyA8LSBzZWxlY3RfaWYoQ2x1c3REYXRhLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGNvbCkgaXMubnVtZXJpYyhjb2wpIHwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLmludGVnZXIoY29sKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgIyBzY2FsZSgpICU+JSAKICAjIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgIyBuYS5vbWl0KCkKCiMgc3RyKENsdXN0RGF0YV9Ob0ZhY3QpCiMgc3VtbWFyeShDbHVzdERhdGFfTm9GYWN0KQoKYGBgCgoKUENBCmBgYHtyfQoKVHJuc2ZybSA8LSBwcmVQcm9jZXNzKHNlbGVjdChSb3V0ZVN0YXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9IGMoIkJveENveCIsICJjZW50ZXIiLCAic2NhbGUiLCAicGNhIikKICAgICAgICAgICAgICAgICAgICAgKQoKIyBsb2FkaW5ncwpUcm5zZnJtJHJvdGF0aW9uCgpSb3V0ZVN0YXRzX1BjYSA8LSBwcmVkaWN0KFRybnNmcm0sIFJvdXRlU3RhdHMpICU+JSAKICBzZWxlY3QoLVJvdXRlKQpSb3V0ZVN0YXRzX1BjYQoKYGBgCgoKQ2x1c3RlcmluZy4KCkFyZSB0aGUgZGF0YSBjbHVzdGVyYWJsZT8KYGBge3J9CgojIyMjIyBBcmUgdGhlIGRhdGEgY2x1c3RlcmFibGU/CiMgZ3JhZGllbnRfY29sIDwtIGxpc3QobG93ID0gInN0ZWVsYmx1ZSIsIGhpZ2ggPSAid2hpdGUiKQpDbHVzdERhdGFfRW5kcyA8LSBnZXRfY2x1c3RfdGVuZGVuY3koUm91dGVTdGF0c19QY2EsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbnJvdyhSb3V0ZVN0YXRzX1BjYQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIC0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ3JhZGllbnQgPSBncmFkaWVudF9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gMTIzNDU2Nzg5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnN0cihDbHVzdERhdGFfRW5kcykKCiMgSG9wa2lucyBzdGF0aXN0aWMKQ2x1c3REYXRhX0VuZHMkaG9wa2luc19zdGF0ICAjIHZhbHVlIG9mIDAuMTY1NzQ5NCBpbXBsaWVzIHRoYXQgdGhlIGRhdGEgYXJlIG5vdCB1bmlmb3JtbHkgZGlzdHJpYnV0ZWQgKHRoZXkgYXJlICJjbHVzdGVyYWJsZSIpCgojcGxvdApDbHVzdERhdGFfRW5kcyRwbG90CgpgYGAKCgpDbHVzdGVyaW5nLiBIb3cgbWFueSBjbHVzdGVycyBhcmUgdGhlcmU/CgprbWVhbnMsIHBhbSwgYW5kIGhpZXJhcmNoaWNhbCBjbHVzdHJpbmcgbWV0aG9kcywgdXNpbmcgd2l0aGluIHN1bSBvZiBzcXVhcmVzIGFuZCBzaWxob3VldHRlIG1lYXN1cmVzLgpgYGB7cn0KCiMgY2xhc3MoUm91dGVTdGF0c19QY2EpCgpmdml6X25iY2x1c3QoUm91dGVTdGF0c19QY2EsIGttZWFucywgbWV0aG9kID0gIndzcyIpICAjIH44IGNsdXN0ZXJzCmZ2aXpfbmJjbHVzdChSb3V0ZVN0YXRzX1BjYSwgcGFtLCBtZXRob2QgPSAid3NzIikgICMgfjYgY2x1c3RlcnMKZnZpel9uYmNsdXN0KFJvdXRlU3RhdHNfUGNhLCBoY3V0LCBtZXRob2QgPSAid3NzIikgICMgfjYgY2x1c3RlcnMKCmZ2aXpfbmJjbHVzdChSb3V0ZVN0YXRzX1BjYSwga21lYW5zLCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpICAjIDIgY2x1c3RlcnMKZnZpel9uYmNsdXN0KFJvdXRlU3RhdHNfUGNhLCBwYW0sIG1ldGhvZCA9ICJzaWxob3VldHRlIikgICMgMiBjbHVzdGVycwpmdml6X25iY2x1c3QoUm91dGVTdGF0c19QY2EsIGhjdXQsIG1ldGhvZCA9ICJzaWxob3VldHRlIiwKICAgICAgICAgICAgIGhjX21ldGhvZCA9ICJjb21wbGV0ZSIpICAjIDIgY2x1c3RlcnMKCmBgYAoKCkNsdXN0ZXJpbmcuIEhvdyBtYW55IGNsdXN0ZXJzIGFyZSB0aGVyZT8KCmttZWFucyBtZXRob2Qgd2l0aCB0aGUgZ2FwIHN0YXRpc3RpYywgdXNpbmcgYm9vdHN0cmFwLgpgYGB7cn0KCiMgQ29tcHV0ZSBnYXAgc3RhdGlzdGljCiMga21lYW5zIHZlcnNpb24Kc2V0LnNlZWQoMTIzNDU2Nzg5KQojIHN5c3RlbS50aW1lKApnYXBfc3RhdF9rbSA8LSBjbHVzR2FwKFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGttZWFucywKICAgICAgICAgICAgICAgICAgICAgICBuc3RhcnQgPSAyNSwKICAgICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgIEIgPSA1MDAKICAgICAgICAgICAgICAgICAgICAgICkKIyApCgojIFByaW50CnByaW50KGdhcF9zdGF0X2ttLCBtZXRob2QgPSAiVGliczIwMDFTRW1heCIpCnByaW50KGdhcF9zdGF0X2ttKQoKCiMgcGFtIHZlcnNpb24Kc2V0LnNlZWQoMTIzNDU2Nzg5KQpnYXBfc3RhdF9wbSA8LSBjbHVzR2FwKFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IHBhbSwKICAgICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgIEIgPSA1MDAKICAgICAgICAgICAgICAgICAgICAgICkKCiMgUHJpbnQKcHJpbnQoZ2FwX3N0YXRfcG0sIG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKcHJpbnQoZ2FwX3N0YXRfcG0pCgoKIyBoaWVyYXJjaGljYWwgdmVyc2lvbgpzZXQuc2VlZCgxMjM0NTY3ODkpCmdhcF9zdGF0X2hjdXQgPC0gY2x1c0dhcChSb3V0ZVN0YXRzX1BjYSwKICAgICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGhjdXQsCiAgICAgICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgQiA9IDUwMAogICAgICAgICAgICAgICAgICAgICAgICApCgojIFByaW50CnByaW50KGdhcF9zdGF0X2hjdXQsIG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKcHJpbnQoZ2FwX3N0YXRfaGN1dCkKCgoKIyBQbG90IGttZWFucwpmdml6X2dhcF9zdGF0KGdhcF9zdGF0X2ttLCAKICAgICAgICAgICAgICBtYXhTRSA9IGxpc3QobWV0aG9kID0gIlRpYnMyMDAxU0VtYXgiKQogICAgICAgICAgICAgKSAgIyAxIGNsdXN0ZXIKCiMgUGxvdCBwYW0KZnZpel9nYXBfc3RhdChnYXBfc3RhdF9wbSwgCiAgICAgICAgICAgICAgbWF4U0UgPSBsaXN0KG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKICAgICAgICAgICAgICkgICMgMiBjbHVzdGVyCgojIFBsb3QgaGllcmFyY2hpY2FsCmZ2aXpfZ2FwX3N0YXQoZ2FwX3N0YXRfaGN1dCwgCiAgICAgICAgICAgICAgbWF4U0UgPSBsaXN0KG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKICAgICAgICAgICAgICkgICMgMSBjbHVzdGVyCgpgYGAKCgpDbHVzdGVyaW5nLiBIb3cgbWFueSBjbHVzdGVycyBhcmUgdGhlcmU/CgprbWVhbnMgbWV0aG9kIHdpdGggdmFyaW91cyBkaWZmZXJlbnQgc3RhdGlzdGljcy4KYGBge3J9CgojIHN0cihpcmlzKQoKbmIgPC0gTmJDbHVzdChSb3V0ZVN0YXRzX1BjYSwgI3NjYWxlKGlyaXNbICwtNV0pLAogICAgICAgICAgICAgIGRpc3RhbmNlID0gImV1Y2xpZGVhbiIsCiAgICAgICAgICAgICAgbWluLm5jID0gMiwKICAgICAgICAgICAgICBtYXgubmMgPSAxNSwKICAgICAgICAgICAgICBtZXRob2QgPSAia21lYW5zIiwKICAgICAgICAgICAgICBpbmRleCA9ICJhbGwiCiAgICAgICAgICAgICApCgpmdml6X25iY2x1c3QobmIpICsgdGhlbWVfbWluaW1hbCgpCgpgYGAKCgpDbHVzdGVyaW5nLiBIb3cgbWFueSBjbHVzdGVycyBhcmUgdGhlcmU/CgpIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBtZXRob2QuIFBhcnRpY3VsYXJseSBsb29raW5nIGF0IHNpbGhvdWV0dGUgc3RhdGlzdGljcy4KYGBge3J9CgojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCBjdXQgaW4gMiB0byAxNSBncm91cHMKZm9yKGkgaW4gMjoxNSkgewogIGFzc2lnbihwYXN0ZTAoIkhDUmVzX0siLCBpKSwKICAgICAgICAgZWNsdXN0KFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgImhjbHVzdCIsCiAgICAgICAgICAgICAgICBrID0gaSwKICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjb21wbGV0ZSIsCiAgICAgICAgICAgICAgICBncmFwaCA9IEZBTFNFCiAgICAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgYXNzaWduKCJ4IiwKICAgICAgICAgZ2V0KHBhc3RlMCgiSENSZXNfSyIsIGkpCiAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgYXNzaWduKHBhc3RlMCgiSENTdGF0c19LIiwgaSksCiAgICAgICAgIGNsdXN0ZXIuc3RhdHMoZGlzdChSb3V0ZVN0YXRzX1NjYWxlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ImV1Y2xpZGVhbiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICB4JGNsdXN0ZXIKICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgYXNzaWduKCJ5IiwKICAgICAgICAgZ2V0KHBhc3RlMCgiSENTdGF0c19LIiwgaSkKICAgICAgICAgICAgKQogICAgICAgICkKICAKICBhc3NpZ24ocGFzdGUwKCJIQ0RlbmRfSyIsIGkpLAogICAgICAgICBmdml6X2RlbmQoeCwgcmVjdCA9IFRSVUUsIHNob3dfbGFiZWxzID0gRkFMU0UpCiAgICAgICAgKQogIAogIGFzc2lnbihwYXN0ZTAoIkhDU2lsX0siLCBpKSwKICAgICAgICAgZnZpel9zaWxob3VldHRlKHgpCiAgICAgICAgKQogIAogIGFzc2lnbihwYXN0ZTAoIkhDU2lsV2lkdGhfSyIsIGkpLAogICAgICAgICBhcy5kYXRhLmZyYW1lKHkkY2x1cy5hdmcuc2lsd2lkdGhzKSAlPiUgCiAgICAgICAgICAgbXV0YXRlKEtWYWwgPSAxOm5yb3coLikKICAgICAgICAgICAgICAgICApCiAgICAgICAgKQogIH0KCgpIQ1NpbFdpZHRoX0FsbEsgPC0gbGVmdF9qb2luKHNlbGVjdChIQ1NpbFdpZHRoX0sxNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgS1ZhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYHkkY2x1cy5hdmcuc2lsd2lkdGhzYAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSENTaWxXaWR0aF9LMTQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIGxlZnRfam9pbiguLAogICAgICAgICAgICBIQ1NpbFdpZHRoX0sxMywKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LMTIsCiAgICAgICAgICAgIGJ5ID0gYygiS1ZhbCIgPSAiS1ZhbCIpCiAgICAgICAgICAgKSAlPiUgCiAgbGVmdF9qb2luKC4sCiAgICAgICAgICAgIEhDU2lsV2lkdGhfSzExLAogICAgICAgICAgICBieSA9IGMoIktWYWwiID0gIktWYWwiKQogICAgICAgICAgICkgJT4lIAogIGxlZnRfam9pbiguLAogICAgICAgICAgICBIQ1NpbFdpZHRoX0sxMCwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LOSwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LOCwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNywKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNiwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNSwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNCwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LMywKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LMiwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApCiAgCmNvbG5hbWVzKEhDU2lsV2lkdGhfQWxsSykgPC0gYygiS1ZhbCIsICJLMTUiLCAiSzE0IiwgIksxMyIsICJLMTIiLCAiSzExIiwgIksxMCIsICJLOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSzgiLCAiSzciLCAiSzYiLCAiSzUiLCAiSzQiLCAiSzMiLCAiSzIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCgojIFZpc3VhbGl6ZQpIQ0RlbmRfSzIKSENEZW5kX0szCkhDRGVuZF9LNApIQ0RlbmRfSzUKSENEZW5kX0s2CkhDRGVuZF9LNwpIQ0RlbmRfSzgKSENEZW5kX0s5CkhDRGVuZF9LMTAKSENEZW5kX0sxMQpIQ0RlbmRfSzEyCkhDRGVuZF9LMTMKSENEZW5kX0sxNApIQ0RlbmRfSzE1CgpIQ1NpbF9LMgpIQ1NpbF9LMwpIQ1NpbF9LNApIQ1NpbF9LNQpIQ1NpbF9LNgpIQ1NpbF9LNwpIQ1NpbF9LOApIQ1NpbF9LOQpIQ1NpbF9LMTAKSENTaWxfSzExCkhDU2lsX0sxMgpIQ1NpbF9LMTMKSENTaWxfSzE0CkhDU2lsX0sxNQoKCkhDU2lsV2lkdGhfQWxsSwoKYGBgCgoKVXNpbmcga21lYW5zLCBQQU0sIGFuZCBIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBtZXRob2RzLCB3ZSBjYW4gc2F5IHdlIHByb2JhYmx5IGhhdmUgYXJvdW4gMiBjbHVzdGVycy4KCkxldCdzIHRyeSBkZW5zaXR5IGNsdXN0ZXJpbmcuICAoVGhpcyB0ZW5kcyB0byBzaG93IHRoYXQgbWF5YmUgdGhlcmUgaXMgb25seSBvbmUgImNsdXN0ZXIsIiBtZWFuaW5nIHRoYXQgZGF0YSBhcmUgbm90IGNsdXN0ZXJhYmxlLikKYGBge3J9CgpybShsaXN0ID0gbHMocGF0dGVybiA9ICJfSyIpCiAgKQoKCiMgQ29tcHV0ZSBEQlNDQU4gdXNpbmcgZnBjIHBhY2thZ2UKa05OZGlzdHBsb3QoUm91dGVTdGF0c19QY2EsIGsgPSAxMCkKYWJsaW5lKGggPSA4LjUsIGx0eSA9IDIpCgpzZXQuc2VlZCgxMjM0NTY3ODkpCmRiIDwtIGZwYzo6ZGJzY2FuKFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgICBlcHMgPSA4LjUsCiAgICAgICAgICAgICAgICAgIE1pblB0cyA9IDEwCiAgICAgICAgICAgICAgICApCgpzdHIoZGIpCmRiCgojIFBsb3QgREJTQ0FOIHJlc3VsdHMKZnZpel9jbHVzdGVyKGRiLAogICAgICAgICAgICAgUm91dGVTdGF0c19QY2EsCiAgICAgICAgICAgICBzdGFuZCA9IEZBTFNFLAogICAgICAgICAgICAgZnJhbWUgPSBGQUxTRSwKICAgICAgICAgICAgIGdlb20gPSAicG9pbnQiCiAgICAgICAgICAgICkKCmBgYAoKCgoKSW52ZXN0aWdhdGluZyBUcmF2ZWxUaW1lX1NlYy4KYGBge3J9CgpWaWV3KGZpbHRlcihUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgUnRlQ2hhbmdlMiA9PSAiU2FtZSIKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxUaW1lX1NlYyksCiAgICAgICAgICAgICAgIFNwZWVkQXZnX01waF9OZXdIdnJzCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKCiMgZXhhbXBsZXMgd2hlcmUgVHJhdmVsVGltZV9TZWMgaXMgc21hbGwgKDEgc2VjKSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgbGFyZ2UuClZpZXcoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAjIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgLShURF9NaV9xMjpURF9NaV9TU0hHX0NudF9GKSwKICAgICAgICAgICAgLShUVF9Icl9xMjpUVF9Icl9TU0hHX0NudF9GKQogICAgICAgICAgICkgJT4lIAogICAgICAgZmlsdGVyKChSb3dOdW1fT0cgPj0gMjIxNzM1MyAmIFJvd051bV9PRyA8PSAyMjE3MzczKSB8ICMgMjIxNzM2MwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMDkwMzIxICYgUm93TnVtX09HIDw9IDMwOTAzNDEpIHwgIyAzMDkwMzMxCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDgwNzY0ICYgUm93TnVtX09HIDw9IDgwNzg0KSB8ICMgODA3NzQKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMzM4NDAgJiBSb3dOdW1fT0cgPD0gMzM4NjApICMgMzM4NTAKICAgICAgICAgICApCiAgICApCgoKCgoKCiMgZXhhbXBsZXMgd2hlcmUgVHJhdmVsVGltZV9TZWMgaXMgbGFyZ2UgYW5kIFNwZWVkQXZnX01waF9OZXdIdnJzIGlzIHNtYWxsLgpWaWV3KGZpbHRlcihUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyMjUwMjkwICYgUm93TnVtX09HIDw9IDIyNTAzMTApIHwgIyAyMjUwMzAwCiAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4Njc3MTcgJiBSb3dOdW1fT0cgPD0gODY3NzM3KSB8ICMgODY3NzI3CiAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4NjQzNzkgJiBSb3dOdW1fT0cgPD0gODY0Mzk5KSB8ICMgODY0Mzg5CiAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4MDgzOTUgJiBSb3dOdW1fT0cgPD0gODA4NDE1KSAjIDgwODQwNQogICAgICAgICAgICkKICAgICkKYGBgCgoKCgpgYGB7cn0KCiAgICAgICAgIAogICAgICAgICAKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyB1bnVzdWFsbHkgc21hbGwgKHdpdGggVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIGxhcmdlKS4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDEwNDIyMjggJiBSb3dOdW1fT0cgPD0gMTA0MjI0OCkgfCAjIDEwNDIyMzgKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gNTM4MTYgJiBSb3dOdW1fT0cgPD0gNTM4MzYpIHwgIyA1MzgyNgogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzNjA1NzEgJiBSb3dOdW1fT0cgPD0gMzYwNTkxKSB8ICMgMzYwNTgxCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDUwMjI3MSAmIFJvd051bV9PRyA8PSA1MDIyOTEpICMgNTAyMjgxIChjYW4ndCBleHBsaWFuIHRoZSB3ZWlyZCBUcmF2ZWxUaW1lX1NlYyBjYWxjdWxhdGlvbiBoZXJlIC0gaXQncyBub3QgZXZlbiBhbiBpbnRlZ2VyISkKICAgICAgICAgICApCiAgICApCgojIHN0aWxsIHRyeWluZyB0byBleHBsYWluIDUwMjI4MS4uLm9uIHRoZSBkYXkgb2YgdGhpcyB3ZWlyZG5lc3MsIHRoZSBidXMgd2FzIG9ubHkgaW4gY2lyY3VsYXRpb24gZm9yIDQtNSBzdG9wcyAofjIwIG1pbnV0ZXMpIG9uIHRoYXQgZGF5IChPY3QgNikKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICBCdXNfSUQgPT0gMjcxMQogICAgICAgICAgICkKICAgICkKCgojIGV4cGxvcmluZyBsYXJnZSB2YWx1ZXMgZm9yIFRyYXZlbFRpbWVfU2VjClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPT0gMzAwCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoVHJhdmVsVGltZV9TZWMpLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyCiAgICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyB1bnVzdWFsbHkgbGFyZ2UgKHdpdGggVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIHNtYWxsLCBzbyBTcGVlZEF2Z19NcGggdmFsdWVzIGFyZSB2ZXJ5IHNtYWxsKS4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDI2Mjc0NTkgJiBSb3dOdW1fT0cgPD0gMjYyNzQ3OSkgfCAjIDI2Mjc0NjkKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjE5MzM0NCAmIFJvd051bV9PRyA8PSAyMTkzMzY0KSB8ICMgMjE5MzM1NAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjQ0MTIzICYgUm93TnVtX09HIDw9IDE2NDQxNDMpIHwgIyAxNjQ0MTMzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2OTYwMCAmIFJvd051bV9PRyA8PSA4Njk2MjApICMgODY5NjEwCiAgICAgICAgICAgKQogICAgKQoKYGBgCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIKClZpZXcoU3BlZWRfUGN0aWxlcyk6IDkwJSBvZiBTcGVlZEF2Z19NcGgyIGFyZSBiZXR3ZWVuIH4zbXBoIGFuZCB+NjZtcGguCmBgYHtyfQoKU3BlZWRfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShBbGxEYXlzX05ld1RyYXZlbERpc3QkU3BlZWRBdmdfTXBoMikgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShBbGxEYXlzX05ld1RyYXZlbERpc3QkU3BlZWRBdmdfTXBoMiwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKEFsbERheXNfTmV3VHJhdmVsRGlzdCRTcGVlZEF2Z19NcGgyKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkU3BlZWRBdmdfTXBoMiksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhTcGVlZF9OdGlsZSlbMV0gPC0gIlNwZWVkQXZnX01waDIiCnN0cihTcGVlZF9OdGlsZSkKClNwZWVkX050aWxlX1Jvd3MgPC0gbnJvdyhTcGVlZF9OdGlsZSkKClZpZXcodGFpbChTcGVlZF9OdGlsZSwgNTAwKSkKCgpTcGVlZF9QY3RpbGVzIDwtIGdyb3VwX2J5KFNwZWVkX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblNwZWVkQXRQY3RpbGUgPSBtaW4oU3BlZWRBdmdfTXBoMiksCiAgICBDbnRzQXRQY3RpbGUgPSBuKCksCiAgICBQY3RzQXRQY3RpbGUgPSBDbnRzQXRQY3RpbGUgLyBTcGVlZF9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKEN1bVN1bVBBdFAgPSBjdW1zdW0oUGN0c0F0UGN0aWxlKQogICAgICAgICkKClZpZXcoU3BlZWRfUGN0aWxlcykKCmBgYAoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKRXhwbG9yaW5nIHRoZSByZW1vdmFsIG9mIG91dGxpZXIgVHJhdmVsVGltZV9TZWMgYW5kIFRyYXZlbERpc3RhbmNlX01pLgpgYGB7cn0KCnN1bW1hcnkoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gMC4wMDAxODkzOTM5ICYgIyBsb3dlc3Qgbm9uLXplcm8gcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA8IDEuMDgxMjUwMDAwMCAmICMgOTl0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gMTAuMDUwMDAwICYgIyAybmQgcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA8IDI5My4wMDAwMDAgIyA5OHRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyCiAgICAgICAgICAgICAgKQogICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKSGlzdG9ncmFtIG9mIFNwZWVkQXZnX01waDIuCmBgYHtyfQoKU3BlZWRfSGlzdERlbiA8LSBnZ3Bsb3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShTcGVlZEF2Z19NcGgyKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFNwZWVkQXZnX01waDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fbGluZShzdGF0ID0gImRlbnNpdHkiLCBjb2xvdXIgPSAicmVkIikgKwogIHN0YXRfYmluKGJpbndpZHRoID0gNSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAyLjUsCiAgICAgICAgICAgdmp1c3QgPSAxLjUsCiAgICAgICAgICAgYWVzKGxhYmVsID0gZm9ybWF0KC4uY291bnQuLiwgYmlnLm1hcmsgPSAiLCIpCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICkgKwogICMgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZvcm1hdCguLmNvdW50Li4sIGJpZy5tYXJrID0gIiwiKQogICMgICAgICAgICAgICAgICksCiAgIyAgICAgICAgICAgc2l6ZSA9IDMsCiAgIyAgICAgICAgICAgbnVkZ2VfeSA9ICguLmNvdW50Li4gKiAwLjEpCiAgIyAgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNzApLCB5bGltID0gYygwLCAwLjA0KQogICAgICAgICAgICAgICAgICkgKwogICMgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIlZhcmlhdGlvbiBpbiBUcmF2ZWwgU3BlZWQiLAogICAgICAgeCA9ICJBdmVyYWdlIFNwZWVkIChtcGgpIiwKICAgICAgIHkgPSAiRGVuc2l0eSIKICAgICAgKQoKU3BlZWRfSGlzdERlbgoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKSGlzdG9ncmFtIG9mIFNwZWVkQXZnX01waDIgYWZ0ZXIgcmVtb3Zpbmcgb3V0bGllciBUcmF2ZWxUaW1lX1NlYyBhbmQgVHJhdmVsRGlzdGFuY2VfTWkuCmBgYHtyfQoKVmlldyhUcmF2RGlzdE1pTmV3X1BjdGlsZXMpClZpZXcoVHJhdlRpbWVIcl9QY3RpbGVzKQoKU3BlZWROb091dGxpZXJfSGlzdERlbiA8LSBnZ3Bsb3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShTcGVlZEF2Z19NcGgyKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA+IDAuMDc3ODQxMDA1ICYgIyA1dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX01pX05ldyA8IDEuMDgxMjUwMDAwMCAmICMgOTl0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gMTIuMTAwMDAwICMgNHRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxUaW1lX1NlYyA8IDI5My4wMDAwMDAgIyA5OHRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBTcGVlZEF2Z19NcGgyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC4uZGVuc2l0eS4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICBzdGF0X2JpbihiaW53aWR0aCA9IDUsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgIHZqdXN0ID0gMS41LAogICAgICAgICAgIGFlcyhsYWJlbCA9IGZvcm1hdCguLmNvdW50Li4sIGJpZy5tYXJrID0gIiwiKQogICAgICAgICAgICAgICksCiAgICAgICAgICApICsKICAjIGdlb21fdGV4dChhZXMobGFiZWwgPSBmb3JtYXQoLi5jb3VudC4uLCBiaWcubWFyayA9ICIsIikKICAjICAgICAgICAgICAgICApLAogICMgICAgICAgICAgIHNpemUgPSAzLAogICMgICAgICAgICAgIG51ZGdlX3kgPSAoLi5jb3VudC4uICogMC4xKQogICMgICAgICAgICAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDcwKSwgeWxpbSA9IGMoMCwgMC4wNCkKICAgICAgICAgICAgICAgICApICsKICAjICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gVHJhdmVsIFNwZWVkIiwKICAgICAgIHN1YnRpdGxlID0gIihyZW1vdmVkIGxvdyBvdXRsaWVycyBvZiBUcmF2ZWwgRGlzdGFuY2UgYW5kIFRyYXZlbCBUaW1lKSIsCiAgICAgICB4ID0gIkF2ZXJhZ2UgU3BlZWQgKG1waCkiLAogICAgICAgeSA9ICJEZW5zaXR5IgogICAgICApCgpTcGVlZE5vT3V0bGllcl9IaXN0RGVuCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIuCgpOZXcgZGF0YXNldCAoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUpIHdoZW4gcmVtb3Zpbmcgb3V0bGllciBsb3cgdmFsdWVzIG9mIFRyYXZlbERpc3RhbmNlX01pX05ldyBhbmQgVHJhdmVsVGltZV9TZWMuCmBgYHtyfQoKVmlldyhUcmF2RGlzdE1pTmV3X1BjdGlsZXMpClZpZXcoVHJhdlRpbWVIcl9QY3RpbGVzKQoKTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUgPC0gZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA+IC4wNzc4NDEwMDUgJiAjIDV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWlfTmV3IDwgMS4wODEyNTAwMDAwICYgIyA5OXRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiAxMi4xMDAwMDAgIyA0dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbFRpbWVfU2VjIDwgMjkzLjAwMDAwMCAjIDk4dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpucm93KEFsbERheXNfTmV3VHJhdmVsRGlzdCkgLSBucm93KE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKQoKc3RyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKQpzdW1tYXJ5KE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcHBlZEF2Z19NcGgyLgoKVmlldyhTcGVlZF9Ob091dF9QY3RpbGVzKTogIEFwcm94aW1hdGVseSA5MCUgb2YgU3BlZWRBdmdfTXBoMiB2YWx1ZXMgYXJlIGJldHdlZW4gfjRtcGggYW5kIH41Nm1waC4KYGBge3J9CgpTcGVlZF9Ob091dF9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lJFNwZWVkQXZnX01waDIpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUkU3BlZWRBdmdfTXBoMiwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lJFNwZWVkQXZnX01waDIpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lJFNwZWVkQXZnX01waDIpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApIAoKY29sbmFtZXMoU3BlZWRfTm9PdXRfTnRpbGUpWzFdIDwtICJTcGVlZEF2Z19NcGgyIgpzdHIoU3BlZWRfTm9PdXRfTnRpbGUpCgpTcGVlZF9Ob091dF9OdGlsZV9Sb3dzIDwtIG5yb3coU3BlZWRfTm9PdXRfTnRpbGUpCgpWaWV3KHRhaWwoU3BlZWRfTm9PdXRfTnRpbGUsIDUwMCkpCgoKU3BlZWRfTm9PdXRfUGN0aWxlcyA8LSBncm91cF9ieShTcGVlZF9Ob091dF9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5TcGVlZEF0UGN0aWxlID0gbWluKFNwZWVkQXZnX01waDIpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gU3BlZWRfTm9PdXRfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSkKICAgICAgICApCgpWaWV3KFNwZWVkX05vT3V0X1BjdGlsZXMpCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwcGVkQXZnX01waDIuCgpFeGxvcmluZyBvZGQvaW1wb3NzaWJsZSB2YWx1ZXMuCmBgYHtyfQoKIyBFeHBsb3Jpbmcgd2hlbiBTcGVlZEF2Z19NcGgyIGlzIE5BICAtLSAgZG9lcyBub3Qgb2NjdXIgYXQgYWxsCm5yb3coZmlsdGVyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lLAogICAgICAgICAgICBpcy5uYShTcGVlZEF2Z19NcGgyKQogICAgICAgICAgICkKICAgICkKCgojIEV4cGxvcmluZyB3aGVuIFNwZWVkQXZnX01waDIgaXMgemVybyAgLS0gIGRvZXMgbm90IG9jY3VyIGF0IGFsbApucm93KGZpbHRlcihOb091dGxpZXJzX1RyYXZlbERpc3ROVGltZSwKICAgICAgICAgICAgU3BlZWRBdmdfTXBoMiA9PSAwCiAgICAgICAgICAgKQogICAgKQoKCiMgZXhhbXBsZXMgd2hlcmUgU3BlZWRBdmdfTXBoMiA8IDMuMjg0ODc3MApWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIFNwZWVkQXZnX01waDIgPiAwICYKICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyIDwgMy4yODQ4NzcwCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFNwZWVkQXZnX01waDIpCiAgICApCgojIGV4YW1wbGVzIHdoZXJlIFNwZWVkQXZnX01waDIgPCAzLjI4NDg3NzAKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDQ4NTMzOCAmIFJvd051bV9PRyA8PSA0ODUzNTgpIHwgIyA0ODUzNDggIC0tICBFeHRyZW1lIHRyYXZlbCB0aW1lLCBSb3V0ZSBDaGFuZ2UKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMzQ2OTUyICYgUm93TnVtX09HIDw9IDM0Njk3MikgfCAjIDM0Njk2MiAgLS0gRXh0cmVtZSB0cmF2ZWwgdGltZSwgUm91dGUgQ2hhbmdlIAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA3MDQ5NCAmIFJvd051bV9PRyA8PSA3MDUxNCkgfCAjIDcwNTA0ICAtLSAgRXh0cmVtZSB0cmF2ZWwgdGltZSwgUm91dGUgQ2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDIwNTE4NDYgJiBSb3dOdW1fT0cgPD0gMjA1MTg2NikgIyAyMDUxODU2ICAtLSAgRXh0cmVtZSB0cmF2ZWwgdGltZSwgUm91dGUgQ2hhbmdlCiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKTGltaXQgdGhlIGRhdGFzZXQgYmFzZWQgb24gU3BlZWRBdmdfTXBoMi4KYGBge3J9CgpOb091dGxpZXJzU3BlZWQgPC0gZmlsdGVyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJldHdlZW4oU3BlZWRBdmdfTXBoMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDQuMDY5MzAwLCAjIDV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA1Ni4wNTY1MSAjOTV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICApCgpucm93KE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKSAtIG5yb3coTm9PdXRsaWVyc1NwZWVkKQoKc3VtbWFyeShOb091dGxpZXJzU3BlZWQpCgpgYGAKCgpUcmF2ZWxUaW1lIG5vdyBsb29rcyBsaWtlIGl0IGhhcyBzb21lIG9kZCB2YWx1ZXMgb24gdGhlIGhpZ2ggZW5kLiAgU28gbGV0J3MgbG9vayBhdCB0aG9zZS4KClZpZXcoVHJhdlRpbWVfTm9PdXRfUGN0aWxlcyk6ICBWaXJ0dWFsbHkgYWxsIHRyaXBzIHNob3VsZCB0YWtlIGxlc3MgdGhhbiA1IG1pbnV0ZXMuIChUaGUgOTl0aCBwZXJjZW50aWxlIG9mIG9mIFRyYXZlbFRpbWUgaXMgYXBwcm94aW1hdGVseSA4IG1pbnV0ZXMuKQpgYGB7cn0KClRyYXZUaW1lX05vT3V0X050aWxlIDwtIGFzLmRhdGEuZnJhbWUoTm9PdXRsaWVyc1NwZWVkJFRyYXZlbFRpbWVfSHIpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoTm9PdXRsaWVyc1NwZWVkJFRyYXZlbFRpbWVfSHIsIDEwMCksCiAgICAgICAgIE1pblIgPSBtaW5fcmFuayhOb091dGxpZXJzU3BlZWQkVHJhdmVsVGltZV9IciksCiAgICAgICAgIFBjdFIgPSBwZXJjZW50X3JhbmsoTm9PdXRsaWVyc1NwZWVkJFRyYXZlbFRpbWVfSHIpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApCgpjb2xuYW1lcyhUcmF2VGltZV9Ob091dF9OdGlsZSlbMV0gPC0gIlRyYXZlbFRpbWVfSHIiCnN0cihUcmF2VGltZV9Ob091dF9OdGlsZSkKClRyYXZUaW1lX05vT3V0X050aWxlX1Jvd3MgPC0gbnJvdyhUcmF2VGltZV9Ob091dF9OdGlsZSkKClZpZXcodGFpbChUcmF2VGltZV9Ob091dF9OdGlsZSwgNTAwKSkKCgpUcmF2VGltZV9Ob091dF9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZUaW1lX05vT3V0X050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblRyYXZUaW1lSHJBdFBjdGlsZSA9IG1pbihUcmF2ZWxUaW1lX0hyKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIFRyYXZUaW1lX05vT3V0X050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpLAogICAgICAgICBNaW5UcmF2VGltZVNlY0F0UGN0aWxlID0gTWluVHJhdlRpbWVIckF0UGN0aWxlICogKDYwICogNjApCiAgICAgICAgKQoKVmlldyhUcmF2VGltZV9Ob091dF9QY3RpbGVzKQoKYGBgCgoKSW52ZXN0aWdhdGluZyBvZGQgVHJhdmVsVGltZV9TZWMgdmFsdWVzLgoKVHJpcHMgbG9uZ2VyIHRoYW4gfjggbWludXRlcy4KYGBge3J9CgpWaWV3KGZpbHRlcihOb091dGxpZXJzU3BlZWQsCiAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gNDkxICMgbWluIGF0IHRoZSAxMDB0aCBwZXJjZW50aWxlCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlcyBvZiBUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgdGhhdCBhcmUgbGFyZ2VzdC4KVmlldyhmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAoUm93TnVtX09HID49IDIwNzE3NTkgJiBSb3dOdW1fT0cgPD0gMjA3MTc3OSkgfCAjIDIwNzE3NjkgIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UsIGFuZCBhIDNocisgd2FpdCBiZWZvcmUgdGhlIG5ldyByb3V0ZSBzdGFydHMKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMTQ3MzY4NiAmIFJvd051bV9PRyA8PSAxNDczNzA2KSB8ICMgMTQ3MzY5NiAgLS0gIHJlc3VsdHMgZnJvbSBhIHJvdXRlIGNoYW5nZSwgYW5kIGEgM2hyIHdhaXQgYmVmb3JlIHRoZSBuZXcgcm91dGUgc3RhcnRzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDEyMjI4MjIgJiBSb3dOdW1fT0cgPD0gMTIyMjg0MikgfCAjIDEyMjI4MzIgIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UsIGFuZCBhIDNociB3YWl0IGJlZm9yZSB0aGUgbmV3IHJvdXRlIHN0YXJ0cwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMDQ2MDg5ICYgUm93TnVtX09HIDw9IDMwNDYxMDkpICMgMzA0NjA5OSAgLS0gIHJlc3VsdHMgZnJvbSBhIHJvdXRlIGNoYW5nZSwgYW5kIGEgM2hyIHdhaXQgYmVmb3JlIHRoZSBuZXcgcm91dGUgc3RhcnRzCiAgICAgICAgICAgKQogICAgKQoKCiMgZXhhbXBsZXMgb2YgVHJhdmVsVGltZV9TZWMgdmFsdWVzIHRoYXQgYXJlIHRoZSBzbWFsbGVzdCBvZiB0aGUgbGFyZ2UuClZpZXcoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMDQ0Njg5ICYgUm93TnVtX09HIDw9IDMwNDQ3MDkpIHwgIyAzMDQ0Njk5ICAtLSAgcmVzdWx0cyBmcm9tIGEgcm91dGUgY2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDMwMjIzNTggJiBSb3dOdW1fT0cgPD0gMzAyMjM3OCkgfCAjIDMwMjIzNjggIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjk5MzAxNiAmIFJvd051bV9PRyA8PSAyOTkzMDM2KSB8ICMgMjk5MzAyNiAgLS0gIHJlc3VsdHMgZnJvbSBhIHByZXZpb3VzIHJvdXRlIGNoYW5nZSAoY2hhbmdlIG9jY3VycmVkIGluIGRlbGV0ZWQgcm93KQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyNjgzNzAzICYgUm93TnVtX09HIDw9IDI2ODM3MjMpICMgMjY4MzcxMyAgLS0gIHJlc3VsdHMgZnJvbSBhIHByZXZpb3VzIHJvdXRlIGNoYW5nZSAoY2hhbmdlIG9jY3VycmVkIGluIGRlbGV0ZWQgcm93KQogICAgICAgICAgICkKICAgICkKCmBgYAoKCkxldCdzIGxvb2sgYXQgdGhlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyBhbmQgcm91dGUgY2hhbmdlcyAoRGlyQ2hhbmdlMikuCgpUaGUgOTl0aCBwZXJjZW50aWxlIG9mIFRyYXZlbFRpbWVfU2VjIGZvciBib3RoLCBhbGwgdHJpcHMsIGFuZCBqdXN0IHRob3NlIHRyaXBzIE5PVCBpbnZvbHZpbmcgcm91dGUgY2hhbmdlcyAoRGlyQ2hhbmdlMiA9ICJTYW1lIiksIGlzIGFwcHJveGltYXRlbHkgNW1pbiAoMzAwIHNlYykuCgpOb3RhIEJlbmU6ICBUaGUgcGVyY2VudGlsZSBjYWxjdWxhdGlvbiBoZXJlIGlzIGRlZmluZWQgc2xpZ2h0bHkgZGlmZmVyZW50IHRoYW4gaW4gbW9zdCBvZiB0aGUgYWJvdmUgYW5hbHlzZXMgKHdoaWNoIGdldCB0aGUgbG93ZXN0IHZhbHVlIGluIHRoZSBiaW4gY3JlYXRlZCBieSAxMDAgbnRpbGVzKS4KYGBge3J9CgpzdW1tYXJ5KHNlbGVjdChOb091dGxpZXJzU3BlZWQsCiAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIgPT0gIlNhbWUiCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIgPT0gIkNoYW5nZSIKICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMKICAgICAgICAgICAgICApCiAgICAgICApCgoKVHJhdlRpbWVTZWNfUXRpbGVzX2RmIDwtIGRhdGEuZnJhbWUoUGN0VmFsdWUgPSBzZXEoMCwgMTAwLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWxsID0gc2VxKDEsIDEwMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNhbWUgPSBzZXEoMSwgMTAxLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2hhbmdlID0gc2VxKDEsIDEwMSwgMSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpUcmF2VGltZVNlY19RdGlsZXNfZGZbICwgMl0gPC0gcXVhbnRpbGUoc2VsZWN0KE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gc2VxKDAsIDEsIDAuMDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKClRyYXZUaW1lU2VjX1F0aWxlc19kZlsgLCAzXSA8LSBxdWFudGlsZShzZWxlY3QoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGlyQ2hhbmdlMiA9PSAiU2FtZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKVHJhdlRpbWVTZWNfUXRpbGVzX2RmWyAsIDRdIDwtIHF1YW50aWxlKHNlbGVjdChmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEaXJDaGFuZ2UyID09ICJDaGFuZ2UiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gc2VxKDAsIDEsIDAuMDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKClZpZXcoVHJhdlRpbWVTZWNfUXRpbGVzX2RmKQoKYGBgCgoKTGltaXQgdGhlIGRhdGFzZXQgbm93IGJhc2VkIG9uIFRyYXZlbFRpbWVfU2VjLgpgYGB7cn0KClVwcGVyTGltaXRUcmF2VGltZSA8LSBmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjIDw9IDQ5MSAjIG1pbiBhdCB0aGUgMTAwdGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCm5yb3coTm9PdXRsaWVyc1NwZWVkKSAtIG5yb3coVXBwZXJMaW1pdFRyYXZUaW1lKQoKc3RyKFVwcGVyTGltaXRUcmF2VGltZSkKCnN1bW1hcnkoVXBwZXJMaW1pdFRyYXZUaW1lKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEd2VsbF9UaW1lMiAoaG93IGxvbmcgdGhlIGJ1cyBpcyBhdCBhIHN0b3ApLgoKRGlmZmVyZW5jZXMgYmV0d2VlbiBEd2VsbF9UaW1lIChieSBXTUFUQSkgYW5kIER3ZWxsX1RpbWUyIChieSBtZSkgYXBwZWFyIHRvIGJlIGR1ZSB0byBzd2l0Y2hlcyBpbiBSb3V0ZUFsdC4gV01BVEEgY2FsY3VsYXRlcyBEd2VsbF9UaW1lIGJ5IGFuIHVua25vd24gcHJvY2Vzcy4gVGhlIFdNQVRBIGNhbGN1bGF0aW9uIGlzIGVxdWFsIHRvIG15IGNhbGN1bGF0aW9uLCBleGNlcHQgZm9yIHRoZSByZWNvcmRzIGltbWVkYWl0ZWx5IGJlZm9yZSBhbmQgYWZ0ZXIgYSBSb3V0ZUFsdCBzd2l0Y2ggKERpckNoYW5nZTIpLgpgYGB7cn0KClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIER3ZWxsX1RpbWUgIT0gRHdlbGxfVGltZTIKICAgICAgICAgICApCiAgICApCgoKIyBFeGFtcGxlcyB3aGVyZSB0aGUgRHdlbGxfVGltZSBhbmQgRHdlbGxfVGltZTIgYXJlIGRpZmZlcmVudApWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAoIChSb3dOdW1fT0cgPj0gNjUgJiBSb3dOdW1fT0cgPD0gODUpIHwgIyA3NQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjIgJiBSb3dOdW1fT0cgPD0gMTkyKSB8ICMgMTcyCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDQzMTk1MiAmIFJvd051bV9PRyA8PSA0MzE5NzIpIHwgIyA0MzE5NjIKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gNDM0NTk1ICYgUm93TnVtX09HIDw9IDQzNDYxNSkgIyA0MzQ2MDUgIC0tICB0aGlzIHJlY29yZCBpcyBOT1QgYSByb3V0ZSBzd2l0Y2gsIGJ1dCBkb2VzIGhhcyBhIFNlcXVlbmNlIHN3aXRjaCAoTWU6IHNob3VsZCB0aGVyZSByZWFsbHkgYmUgYSByb3V0ZSBzd2l0Y2ggaGVyZT8pCiAgICAgICAgICAgICkKICAgICAgICAgICApCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIER3ZWxsX1RpbWUyIChob3cgbG9uZyB0aGUgYnVzIGlzIGF0IGEgc3RvcCkuCgpGaXJzdCwgY3JlYXRlIHNvbWUgInJhbmsiIHN0YXRzLgpWaWV3KERUMl9QY3RpbGVzKTogOTUlIG9mIER3ZWxsX1RpbWUycyBhcmUgPD0gMjMgc2Vjb25kcy4uLmJ1dCBzb21lIHdlaXJkIChlLmcuLCBuZWFybHkgMiBob3VyIER3ZWxsX1RpbWUycyBleGlzdCkuCmBgYHtyfQoKRHdlbGxUaW1lMl9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKEFsbERheXNfTmV3T3JkZXIkRHdlbGxfVGltZTIpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdPcmRlciREd2VsbF9UaW1lMiwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKEFsbERheXNfTmV3T3JkZXIkRHdlbGxfVGltZTIpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKEFsbERheXNfTmV3T3JkZXIkRHdlbGxfVGltZTIpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApIAoKY29sbmFtZXMoRHdlbGxUaW1lMl9OdGlsZSlbMV0gPC0gIkR3ZWxsX1RpbWUyIgpzdHIoRHdlbGxUaW1lMl9OdGlsZSkKCkR3ZWxsVGltZTJfTnRpbGVfUm93cyA8LSBucm93KER3ZWxsVGltZTJfTnRpbGUpCgpWaWV3KHRhaWwoRHdlbGxUaW1lMl9OdGlsZSwgNTAwKSkKCgpEd2VsbFRpbWUyX1BjdGlsZXMgPC0gZ3JvdXBfYnkoRHdlbGxUaW1lMl9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluRHdlbGxBdFBjdGlsZSA9IG1pbihEd2VsbF9UaW1lMiksCiAgICBDbnRzQXRQY3RpbGUgPSBuKCksCiAgICBQY3RzQXRQY3RpbGUgPSBDbnRzQXRQY3RpbGUgLyBEd2VsbFRpbWUyX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpCiAgICAgICAgKQoKVmlldyhEd2VsbFRpbWUyX1BjdGlsZXMpCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIER3ZWxsX1RpbWUyIChob3cgbG9uZyB0aGUgYnVzIGlzIGF0IGEgc3RvcCkuCgpIaXN0b2dyYW0gb2YgRHdlbGxfVGltZTIuCmBgYHtyfQoKRHdlbGxUaW1lMl9IaXN0RGVuIDwtIGdncGxvdChBbGxEYXlzX05ld09yZGVyLCBhZXMoeCA9IER3ZWxsX1RpbWUyLCB5ID0gLi5kZW5zaXR5Li4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDEsIDI1KSwgeWxpbSA9IGMoMCwgMC4wNSkKICAgICAgICAgICAgICAgICApICsKICB4bGFiKCJUaW1lIGEgQnVzIFN0YXlzIGF0IGEgU3RvcCAoc2VjKSIpICsgCiAgeWxhYigiRGVuc2l0eSIpICsgCiAgIyAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiVmFyaWF0aW9uIGluIEhvdyBMb25nIGEgQnVzIFN0YXlzIGF0IGEgU3RvcCIKICAgICAgICAgICAgICAgICAgICAgICAgICAjICxhdG9wKGl0YWxpYygieHh4eHgiKSwiIikKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICkKCkR3ZWxsVGltZTJfSGlzdERlbgoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEd2VsbF9UaW1lMiAoaG93IGxvbmcgdGhlIGJ1cyBpcyBhdCBhIHN0b3ApLgoKTG9va2luZyBhdCBzb21lIHdlaXJkbHkgbG9uZyBEd2VsbF9UaW1lMiB2YWx1ZXMuCmBgYHtyfQoKVmlldyhhcnJhbmdlKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICBkZXNjKER3ZWxsX1RpbWUyKQogICAgICAgICAgICApCiAgICApCgoKIyBleGFtcGxlcyBvZiBleHRyZW1lbHkgbGFyZ2UgRHdlbGxfVGltZTJzClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjkyNjY5ICYgUm93TnVtX09HIDw9IDI5MjY4OSkgfCAjIDI5MjY3OQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA1MzEwNTcgJiBSb3dOdW1fT0cgPD0gNTMxMDc3KSB8ICMgNTMxMDY3CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDEzODg2MjcgJiBSb3dOdW1fT0cgPD0gMTM4ODY0NykgfCAjIDEzODg2MzcKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMTY0NTcxMSAmIFJvd051bV9PRyA8PSAxNjQ1NzMxKSAjIDE2NDU3MjEKICAgICAgICAgICApCiAgICApCgoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgRHdlbGxfVGltZTIgPT0gMAogICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpWaWV3KERUMl9QY3RpbGVzKTogOTQlIG9mIERlbHRhX1RpbWUgdmFsdWVzIGFyZSBiZXR3ZWVuIC0yMzYgc2Vjb25kcyBhbmQgMSwyNTkgc2Vjb25kcy4gUm91Z2hseSA2NiUgb2YgcmVjb3JkcyBhcmUgd2l0aGluIDUgbWluIGxhdGUgYW5kIDUgbWluIGVhcmx5Li4uYnV0IHNvbWUgd2VpcmQgKGUuZy4sIGFsbW9zdCA1MCBtaW51dGUgbGF0ZSBvciA0MCBtaW51dGUgZWFybHkpIERlbHRhX1RpbWVzIGV4aXN0LgoKTm90ZSB0aGF0IERlbHRhX1RpbWUgaXMgdGhlIGRpZmZlcmVuY2UgZnJvbSB0aGUgc2NoZWR1bGVkIGJ1cyBhcnJpdmFsLiBTbyBpZiB0d28gYnVzZXMgYXJlIHNjaGVkdWxlZCB0byBhcnJpdmUgYXQgYSBkZXN0aW5hdGlvbiBhdCAxMDowMHBtIGFuZCAxMDoyMHBtLCBhbmQgaWYgdGhlIDEwOjIwcG0gYnVzIGhhcyBhIERlbHRhX1RpbWUgb2YgNSBtaW51dGVzLCB0aGVyZSBhcmUgMjUgbWludXRlcyBiZXR3ZWVuIGJ1cyBhcnJpdmFscyBhdCB0aGUgc3RvcC4KCkFsc28gbm90ZSB0aGF0IGJhc2VkIG9uIGEgY29tbWVudCBhdCBodHRwczovL3BsYW5pdG1ldHJvLmNvbS8yMDE2LzExLzE2L2RhdGEtZG93bmxvYWQtbWV0cm9idXMtdmVoaWNsZS1sb2NhdGlvbi1kYXRhLywgdGhlIERlbHRhX1RpbWUgdmFsdWVzIGRvbid0IGFwcGVhciB0byBjb2luY2lkZSB3aXRoIHB1Ymxpc2hlZCBidXMgc2NoZWR1bGVzIChlLmcuLCB0aGUgWDIgZGVwYXJ0aW5nIGV2ZXJ5IDggbWludXRlcyBkdXJpbmcgcGVhayBob3VycykuCmBgYHtyfQoKRGVsdFRpbWVfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lLCAxMDApLAogICAgICAgICBNaW5SID0gbWluX3JhbmsoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApIAoKY29sbmFtZXMoRGVsdFRpbWVfTnRpbGUpWzFdIDwtICJEZWx0YV9UaW1lIgpzdHIoRGVsdFRpbWVfTnRpbGUpCgpEZWx0VGltZV9OdGlsZV9Sb3dzIDwtIG5yb3coRGVsdFRpbWVfTnRpbGUpCgpWaWV3KHRhaWwoRGVsdFRpbWVfTnRpbGUsIDUwMCkpCgoKRGVsdFRpbWVfUGN0aWxlcyA8LSBncm91cF9ieShEZWx0VGltZV9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5EZWx0VGltZUF0UGN0aWxlID0gbWluKERlbHRhX1RpbWUpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gRGVsdFRpbWVfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSkKICAgICAgICApCgpWaWV3KERlbHRUaW1lX1BjdGlsZXMpCkRlbHRUaW1lX1BjdGlsZXMKCiMgfjY2JSBvZiByb3dzIGFyZSBiZXR3ZWVuIDUgbWluIGxhdGUgYW5kIDUgbWluIGVhcmx5Cm5yb3coZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIERlbHRhX1RpbWUgPj0gLTMwMCAmCiAgICAgICAgICAgICAgRGVsdGFfVGltZSA8PSAzMDAKICAgICAgICAgICApCiAgICApIC8gbnJvdyhBbGxEYXlzX05ld09yZGVyKQoKCiMgZXhhbXBsZXMgb2Ygd2VpcmQgbGFyZ2UgRGVsdGFfVGltZXMKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgRGVsdGFfVGltZSA8IC00MjAyIHwKICAgICAgICAgICAgICBEZWx0YV9UaW1lID4gMTcwNQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKERlbHRhX1RpbWUpCiAgICAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEZWx0YV9UaW1lIChob3cgZWFybHkgb3IgbGF0ZSB0aGUgYnVzIGlzKS4KCkRlbHRhX1RpbWUgaGlzdG9ncmFtLgpgYGB7cn0KCkRlbHRUaW1lX0hpc3REZW4gPC0gZ2dwbG90KEFsbERheXNfTmV3T3JkZXIsIGFlcyh4ID0gKERlbHRhX1RpbWUgLyA2MCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gKDUvNjApLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC01LCA1KSkgKwogIHhsYWIoIkJ1cyBMYXRlbmVzcyAobWluKSIpICsgCiAgeWxhYigiRGVuc2l0eSIpICsgCiAgIyAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiVmFyaWF0aW9uIGluIEhvdyBFYXJseS9MYXRlIGEgQnVzIElzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBhdG9wKGl0YWxpYygiKHBvc2l0aXZlIHZhbHVlcyBhcmUgbGF0ZSBhcnJpdmFscykiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICkKCkRlbHRUaW1lX0hpc3REZW4KCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpEZWx0YV9UaW1lIGJveHBsb3QuCmBgYHtyfQoKIyBDb3VudF9WYWx1ZXMgaXMgbmVlZGVkIHRvIGRpc3BsYXkgdGhlIG1lZGlhbnMgb24gdGhlIGJveCBwbG90cwpDb3VudF9WYWx1ZXMgPC0gZGRwbHkoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgIC4oRXZlbnRfVGltZV9Ickdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX0NvdW50cyA9IG1lZGlhbihEZWx0YV9UaW1lIC8gNjAsIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgICAgKQoKRGVsdFRpbWVfQm94UGxvdCA8LSBnZ3Bsb3QoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVsdGFfVGltZSAvIDYwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFKSArIAogICMgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC0zMDAsIDEyMDApKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC01LCAyMCkpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50X1ZhbHVlcywKICAgICAgICAgICAgYWVzKHkgPSBWYWx1ZV9Db3VudHMsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdChyb3VuZChWYWx1ZV9Db3VudHMsIGRpZ2l0cyA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB4bGFiKCJIb3VyIEdyb3VwIikgKyAKICB5bGFiKCJCdXMgTGF0ZW5lc3MgKG1pbnV0ZXMpIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsgCiAgI3RoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiSG93IEVhcmx5L0xhdGUgaXMgdGhlIEJ1cyAoYnkgSG91ciBHcm91cCkiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGF0b3AoaXRhbGljKCIocG9zaXRpdmUgdmFsdWVzIGFyZSBsYXRlIGFycml2YWxzKSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgKQoKRGVsdFRpbWVfQm94UGxvdAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEZWx0YV9UaW1lIChob3cgZWFybHkgb3IgbGF0ZSB0aGUgYnVzIGlzKS4KCkV4cGxvcmluZyAiZXh0cmVtZSIgRGVsdGFfVGltZXMuICBGaXJzdCBsZXQncyBnZXQgc29tZSAicmFuayIgc3RhdHMuCmBgYHtyfQoKVmlldyhEZWx0VGltZV9QY3RpbGVzKQpEZWx0VGltZV9QY3RpbGVzCgoKRGVsdFRpbWVBYnNfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShhYnMoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSkgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShhYnMoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKGFicyhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhhYnMoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhEZWx0VGltZUFic19OdGlsZSlbMV0gPC0gIkRlbHRhX1RpbWVfQWJzIgpzdHIoRGVsdFRpbWVBYnNfTnRpbGUpCgpEZWx0VGltZUFic19OdGlsZV9Sb3dzIDwtIG5yb3coRGVsdFRpbWVBYnNfTnRpbGUpCgpWaWV3KHRhaWwoRGVsdFRpbWVBYnNfTnRpbGUsIDUwMCkpCgoKRGVsdFRpbWVBYnNfUGN0aWxlcyA8LSBncm91cF9ieShEZWx0VGltZUFic19OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5EZWx0VGltZUF0UGN0aWxlID0gbWluKERlbHRhX1RpbWVfQWJzKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIERlbHRUaW1lX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpCiAgICAgICAgKQoKVmlldyhEZWx0VGltZUFic19QY3RpbGVzKQpEZWx0VGltZUFic19QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIERlbHRhX1RpbWUgKGhvdyBlYXJseSBvciBsYXRlIHRoZSBidXMgaXMpLgoKRXhwbG9yaW5nICJleHRyZW1lIiBEZWx0YV9UaW1lcy4gIFRoZW4gbGV0J3MgY2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIGJ1c2VzIHRoYXQgYXJlIDEwIG1pbnV0ZXMgKG9yIG1vcmUpIGxhdGUvZWFybHkuCmBgYHtyfQoKSHJHcm91cF9EZWx0YVRpbWVfQWxsIDwtIGdyb3VwX2J5KEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKEV2ZW50QWxsX0NudCA9IG4oKQogICAgICAgICAgICkKCnN0cihIckdyb3VwX0RlbHRhVGltZV9BbGwpClZpZXcoSHJHcm91cF9EZWx0YVRpbWVfQWxsKQoKCkhyR3JvdXBfRGVsdGFUaW1lX0Fib3ZlMTBNaW4gPC0gZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFicyhEZWx0YV9UaW1lKSA+PSA2MDAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBncm91cF9ieShFdmVudF9UaW1lX0hyR3JvdXApICU+JSAKICBzdW1tYXJpc2UoRXZlbnRBYm92ZTEwX0NudCA9IG4oKQogICAgICAgICAgICkKCnN0cihIckdyb3VwX0RlbHRhVGltZV9BYm92ZTEwTWluKQpWaWV3KEhyR3JvdXBfRGVsdGFUaW1lX0Fib3ZlMTBNaW4pCgoKSHJHcm91cF9EZWx0YVRpbWVDb21wYXJlIDwtIGlubmVyX2pvaW4oSHJHcm91cF9EZWx0YVRpbWVfQWJvdmUxME1pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSHJHcm91cF9EZWx0YVRpbWVfQWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIkV2ZW50X1RpbWVfSHJHcm91cCIgPSAiRXZlbnRfVGltZV9Ickdyb3VwIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoUGN0RXZlbnRzQWJvdmUxMCA9IEV2ZW50QWJvdmUxMF9DbnQgLyBFdmVudEFsbF9DbnQpCgpWaWV3KEhyR3JvdXBfRGVsdGFUaW1lQ29tcGFyZSkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpRdWlja2x5IHBsb3QgdGhlc2UgImV4dHJlbWUiIERlbHRhX1RpbWVzLiAKYGBge3J9CgpEZWx0VGltZV9BYm92ZTEwX0NvbHMgPC0gZ2dwbG90KEhyR3JvdXBfRGVsdGFUaW1lQ29tcGFyZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdEV2ZW50c0Fib3ZlMTAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2NvbChmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZvcm1hdChyb3VuZChQY3RFdmVudHNBYm92ZTEwLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgbnVkZ2VfeSA9IChIckdyb3VwX0RlbHRhVGltZUNvbXBhcmUkUGN0RXZlbnRzQWJvdmUxMCAqIC0wLjEpCiAgICAgICAgICAgKSArCiAgIyBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTUsIDUpKSArCiAgeGxhYigiSG91ciBHcm91cCIpICsgCiAgeWxhYigiUGVyY2VudCBvZiBBbGwgQnVzIEFycml2YWxzIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJXaGVuIGlzIGEgQnVzIDEwKyBNaW51dGVzIExhdGUvRWFybHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAsYXRvcChpdGFsaWMoInBvc2l0aXZlIHZhbHVlcyBhcmUgbGF0ZSBhcnJpdmFscyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAiIgogICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICkKCkRlbHRUaW1lX0Fib3ZlMTBfQ29scwoKYGBgCgoKUXVpY2sgaW52ZXN0aWdhdGlvbiBvbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gRHdlbGxfVGltZTIgKHRoZSB0aW1lIGEgYnVzIGlzIGF0IGEgc3RvcCkgYW5kIERlbHRhX1RpbWUgKGhvdyBlYXJseS9sYXRlIHRoZSBidXMgaXMpLgoKQ29ycmVsYXRpb24uCmBgYHtyfQoKRHdlbGxURGVsdGFUX0NvcnIgPC0gYXMubWF0cml4KGNvcih4ID0gQWxsRGF5c19OZXdPcmRlciREd2VsbF9UaW1lMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZSA9ICJwYWlyd2lzZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCkR3ZWxsVERlbHRhVF9Db3JyCgpgYGAKCgpRdWljayBpbnZlc3RpZ2F0aW9uIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBEd2VsbF9UaW1lMiAodGhlIHRpbWUgYSBidXMgaXMgYXQgYSBzdG9wKSBhbmQgRGVsdGFfVGltZSAoaG93IGVhcmx5L2xhdGUgdGhlIGJ1cyBpcykuCgpOZXh0LCBsZXQncyBnZXQgYSBzYW1wbGUgb2YgZGF0YSBmb3IgcGxvdHRpbmcuIExldCdzIGRvIHRoaXMgZm9yIHRoZSBmdWxsIGRhdGFzZXQgKEFsbERheXNfTmV3T3JkZXIpLgpgYGB7cn0KCkFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wIDwtIHNhbXBsZV9mcmFjKEFsbERheXNfTmV3T3JkZXIsIDAuMSkgJT4lIAogIHNlbGVjdChEZWx0YV9UaW1lLAogICAgICAgICBEd2VsbF9UaW1lMgogICAgICAgICkgJT4lIAogIG11dGF0ZShEYXRhU2V0ID0gIkFsbERhdGEiKQoKc3RyKEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wKQoKYGBgCgoKUXVpY2sgaW52ZXN0aWdhdGlvbiBvbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gRHdlbGxfVGltZTIgKHRoZSB0aW1lIGEgYnVzIGlzIGF0IGEgc3RvcCkgYW5kIERlbHRhX1RpbWUgKGhvdyBlYXJseS9sYXRlIHRoZSBidXMgaXMpLgoKTGV0J3MgYWxzbyBnZXQgYSBzYW1wbGUgb2YgZGF0YSBmb3IgcGxvdHRpbmcsIGJ1dCB3aXRoIGEgZGF0c2V0IHRoYXQgcmVtb3ZlcyBvdXRsaWVycy4KYGBge3J9CgpWaWV3KERlbHRUaW1lX1BjdGlsZXMpClZpZXcoRHdlbGxUaW1lMl9QY3RpbGVzKQoKQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmV0d2VlbihEZWx0YV9UaW1lLCAtNDAyLCAxNzA1KSAmICMgcmVtb3ZlcyBhYm91dCAyJSBvZiBEZWx0YV9UaW1lIHZhbHVlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJldHdlZW4oRHdlbGxfVGltZTIsIDEsIDYzKSAgIyByZW1vdmVzIGFib3V0IDIlIG9mIER3ZWxsX1RpbWUyIHZhbHVlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNhbXBsZV9mcmFjKDAuMSkgJT4lIAogIHNlbGVjdChEZWx0YV9UaW1lLAogICAgICAgICBEd2VsbF9UaW1lMgogICAgICAgICkgJT4lIAogIG11dGF0ZShEYXRhU2V0ID0gIk91dGxpZXJzUmVtb3ZlZCIpCgpzdHIoQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCkKCmBgYAoKClF1aWNrIGludmVzdGlnYXRpb24gb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIER3ZWxsX1RpbWUyICh0aGUgdGltZSBhIGJ1cyBpcyBhdCBhIHN0b3ApIGFuZCBEZWx0YV9UaW1lIChob3cgZWFybHkvbGF0ZSB0aGUgYnVzIGlzKS4KClBsb3R0aW5nIHRoZSBkYXRhIGZyb20gdGhlIGRhdGFzZXQgdGhhdCBkb2VzIG5vdCByZW1vdmUgb3V0bGllcnMuCmBgYHtyfQoKRHdlbGxURGVsdGFUX1NjYXR0ZXIgPC0gZ2dwbG90KEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKER3ZWxsX1RpbWUyLCBEZWx0YV9UaW1lKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSwgYWxwaGEgPSAwLjUpICsKICBzY2FsZV9zaGFwZShzb2xpZCA9IEZBTFNFKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3VyID0gInJlZCIpICsKICAjIHhsYWIoIlRpbWUgYXQgU3RvcCAoc2VjKSIpICsgCiAgIyB5bGFiKCJMYXRlbmVzcyAoc2VjKSIpICsKICBhbm5vdGF0ZShsYWJlbCA9IGxtX2VxbihkZiA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld09yZGVyXzEwUGN0U2FtcCREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBBbGxEYXlzX05ld09yZGVyXzEwUGN0U2FtcCREd2VsbF9UaW1lMgogICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICB4ID0gMjIwMCwKICAgICAgICAgICB5ID0gNjAwLAogICAgICAgICAgIGdlb20gPSAidGV4dCIsCiAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgY29sb3VyID0gInJlZCIsCiAgICAgICAgICAgcGFyc2UgPSBUUlVFCiAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIkxhdGVuZXNzIHZzIFRpbWUgYXQgU3RvcCIsCiAgICAgICBzdWJ0aXRsZSA9ICIobm8gb3V0bGllcnMgcmVtb3ZlZCkiLAogICAgICAgeCA9ICJUaW1lIGF0IFN0b3AgKHNlYykiLAogICAgICAgeSA9ICJMYXRlbmVzcyAoc2VjKSIKICAgICAgKQogICMgZ2d0aXRsZShleHByZXNzaW9uKGF0b3AoIkxhdGVuZXNzIHZzIFRpbWUgYXQgU3RvcCIKICAjICAgICAgICAgICAgICAgICAgICAgICAgICxhdG9wKGl0YWxpYygiKG5vIG91dGxpZXJzIHJlbW92ZWQpIiksCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIgogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICApCiMgKwojICAgZ2VvbV9qaXR0ZXIoKQoKRHdlbGxURGVsdGFUX1NjYXR0ZXIKCmBgYAoKClF1aWNrIGludmVzdGlnYXRpb24gb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIER3ZWxsX1RpbWUyICh0aGUgdGltZSBhIGJ1cyBpcyBhdCBhIHN0b3ApIGFuZCBEZWx0YV9UaW1lIChob3cgZWFybHkvbGF0ZSB0aGUgYnVzIGlzKS4KClBsb3R0aW5nIHRoZSBkYXRhIGZyb20gdGhlIGRhdGFzZXQgdGhhdCBkb2VzIHJlbW92ZSBvdXRsaWVycy4KYGBge3J9CgpEd2VsbFREZWx0YVRfU2NhdHRlcl9Ob0V4dHJlbWVzIDwtIGdncGxvdChBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoRHdlbGxfVGltZTIsIERlbHRhX1RpbWUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIGFscGhhID0gMC41KSArCiAgc2NhbGVfc2hhcGUoc29saWQgPSBGQUxTRSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG91ciA9ICJibHVlIikgKwogICMgeGxhYigiVGltZSBhdCBTdG9wIChzZWMpIikgKyAKICAjIHlsYWIoIkxhdGVuZXNzIChzZWMpIikgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSA1MCwKICAgICAgICAgICB5ID0gLTQ3NSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJibHVlIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIiwKICAgICAgIHN1YnRpdGxlID0gIigyJSBvZiBvdXRsaWVycyByZW1vdmVkKSIsCiAgICAgICB4ID0gIlRpbWUgYXQgU3RvcCAoc2VjKSIsCiAgICAgICB5ID0gIkxhdGVuZXNzIChzZWMpIgogICAgICApCiAgIyBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIgogICMgICAgICAgICAgICAgICAgICAgICAgICAgLGF0b3AoaXRhbGljKCIoMiUgb2Ygb3V0bGllcnMgcmVtb3ZlZCkiKSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICAgICAgICAgICAgICAgICAgKQogICMgICAgICAgICAgICAgICAgICAgKQogICMgICAgICAgICkKIyArCiMgICBnZW9tX2ppdHRlcigpCgpEd2VsbFREZWx0YVRfU2NhdHRlcl9Ob0V4dHJlbWVzCgpgYGAKCgpRdWljayBpbnZlc3RpZ2F0aW9uIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBEd2VsbF9UaW1lMiAodGhlIHRpbWUgYSBidXMgaXMgYXQgYSBzdG9wKSBhbmQgRGVsdGFfVGltZSAoaG93IGVhcmx5L2xhdGUgdGhlIGJ1cyBpcykuCgpQbG90dGluZyB0aGUgZGF0YSBmcm9tIGJvdGggZGF0YXNldHMgdG9nZXRoZXIuCmBgYHtyfQoKQ29tYmluZWREYXRhIDwtIHJiaW5kKEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcAogICAgICAgICAgICAgICAgICAgICApCgpDb21iaW5lZERhdGEkRGF0YVNldCA8LSBmYWN0b3IoQ29tYmluZWREYXRhJERhdGFTZXQpCgpzdHIoQ29tYmluZWREYXRhKQoKCkR3ZWxsVERlbHRhVF9TY2F0dGVyX0NvbWJpbmVkIDwtIGdncGxvdChDb21iaW5lZERhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IER3ZWxsX1RpbWUyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBEZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IERhdGFTZXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIGFscGhhID0gMC41KSArCiAgc2NhbGVfc2hhcGUoc29saWQgPSBGQUxTRSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCA1MDApLCB5bGltID0gYygtMTAwMCwgMjAwMCkKICAgICAgICAgICAgICAgICApICsKICBnZW9tX3Ntb290aChkYXRhID0gZmlsdGVyKENvbWJpbmVkRGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGFTZXQgPT0gIkFsbERhdGEiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwKICAgICAgICAgICAgICBjb2xvdXIgPSAicmVkIgogICAgICAgICAgICAgKSArCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IGZpbHRlcihDb21iaW5lZERhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRhU2V0ID09ICJPdXRsaWVyc1JlbW92ZWQiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwKICAgICAgICAgICAgICBjb2xvdXIgPSAiYmx1ZSIKICAgICAgICAgICAgICkgKwogICMgZmFjZXRfd3JhcCggfiBEYXRhU2V0LCBuY29sID0gMikgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wJERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSAzMDAsCiAgICAgICAgICAgeSA9IC02MDAsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICBjb2xvdXIgPSAicmVkIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSAzMDAsCiAgICAgICAgICAgeSA9IC04MDAsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICBjb2xvdXIgPSAiYmx1ZSIsCiAgICAgICAgICAgcGFyc2UgPSBUUlVFCiAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGxhYnModGl0bGUgPSAiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIiwKICAgICAgIHggPSAiVGltZSBhdCBTdG9wIChzZWMpIiwKICAgICAgIHkgPSAiTGF0ZW5lc3MgKHNlYykiCiAgICAgICkKICAjIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJMYXRlbmVzcyB2cyBUaW1lIGF0IFN0b3AiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAsYXRvcChpdGFsaWMoIjIlIG9mIG91dGxpZXJzIHJlbW92ZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgICAgICIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgICAgICkKICAgICAgICAgIyAgICAgICAgICAgICAgICAgKQogICAgICAgICAjICAgICAgICAgICAgKQogICAgICAgICAjICkKIyArCiMgICBnZW9tX2ppdHRlcigpCgpEd2VsbFREZWx0YVRfU2NhdHRlcl9Db21iaW5lZAoKYGBgCgoKCgoKCgoKCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLgo=